bostrat-node 1.7.6 → 1.7.7
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-changelog +2 -1
- package/bin/bostrat-loop +3 -3
- package/dist/cli.js +60 -60
- package/dist/index.js +175 -173
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,89 +1,91 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var iE=Object.defineProperty;var O=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Ru=(e,t)=>{for(var n in t)iE(e,n,{get:t[n],enumerable:!0})};import{createHash as cE,randomBytes as lE,timingSafeEqual as uE}from"node:crypto";function Qh(){return lE(32).toString("hex")}function js(e){let t=typeof e=="string"?e.trim():"";return t?cE("sha256").update(t).digest("hex"):null}function ew(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:uE(Buffer.from(e),Buffer.from(t))}function Ls(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if($u.set(e,($u.get(e)||0)+1),Ou.has(e)&&n-Ou.get(e)<dE)return!1;Ou.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=${$u.get(e)}${o?` ${o}`:""}`),!0}async function tw(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 Li,Fi,$u,Ou,dE,Fs=O(()=>{Li="BOSTRAT_HOOK_TOKEN",Fi="x-bostrat-hook";$u=new Map,Ou=new Map,dE=6e4});import dt from"node:fs/promises";import nw from"node:os";import ot from"node:path";import{execFile as fE}from"node:child_process";import{promisify as pE}from"node:util";function Ui(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function mE(){return process.env.CLAUDE_CONFIG_DIR?ot.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):ot.join(nw.homedir(),".claude.json")}function hE(){return process.env.CLAUDE_CONFIG_DIR?ot.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):ot.join(nw.homedir(),".claude","settings.json")}async function Ln(e){if(!e||typeof e!="string")return;let t=ot.resolve(e),n=mE(),r=hE(),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(ot.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 "${Fi}: $${Li}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let f of["SessionStart","UserPromptSubmit","Stop"])i.hooks[f]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await dt.mkdir(ot.dirname(r),{recursive:!0}),await dt.writeFile(r,JSON.stringify(i,null,2)),await wE(t)}async function wE(e){try{let t=ot.resolve(e),n=ot.join(t,".bostrat","memory","claude"),r=ot.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(ot.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await dt.mkdir(n,{recursive:!0}),await dt.mkdir(ot.dirname(r),{recursive:!0}),await dt.writeFile(r,JSON.stringify(o,null,2)),await gE(t,["/.claude/settings.local.json"])}catch{}}async function gE(e,t){try{let{stdout:n}=await pE(fE)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=ot.resolve(e,n.trim()),o=await dt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await dt.mkdir(ot.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 Us=O(()=>{Fs()});import Cu from"node:fs/promises";import yE from"node:os";import Hi from"node:path";function bE(){let e=process.env.CODEX_HOME||Hi.join(yE.homedir(),".codex");return Hi.join(e,"config.toml")}function rw(e){return`[projects."${e}"]`}function kE(e,t){let n=rw(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 go(e){if(!e||typeof e!="string")return;let t=Hi.resolve(e),n=bE(),r="";try{r=await Cu.readFile(n,"utf8")}catch{}if(kE(r,t))return;let o=rw(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),f=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
8
|
`;r=r.slice(0,a)+f+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await
|
|
14
|
-
`,"utf8")}var
|
|
13
|
+
`:"")}try{await Cu.mkdir(Hi.dirname(n),{recursive:!0}),await Cu.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Wi=O(()=>{});import ow from"node:fs/promises";import xE from"node:os";import Hs from"node:path";function SE(e){let n=Hs.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Hs.join(xE.homedir(),".cursor");return Hs.join(r,"projects",n)}async function yo(e){if(!e||typeof e!="string")return;let t=Hs.resolve(e),n=SE(t),r=Hs.join(n,".workspace-trusted");await ow.mkdir(n,{recursive:!0}),await ow.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var qi=O(()=>{});import Nu from"node:fs/promises";import Fn from"node:path";import{execFile as vE}from"node:child_process";import{promisify as TE}from"node:util";async function $E(e){try{return(await Nu.stat(Fn.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function OE(e){let t=i=>Nu.readFile(Fn.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 cn(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await $E(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Fn.basename(o),branch:await OE(o)});return}if(s>=IE)return;let i;try{i=await Nu.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||RE.has(c.name)||await t(Fn.join(o,c.name),s+1)}for(let o of _E)await t(Fn.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await me(o.path))?.toLowerCase();if(s){if(n.has(s))continue;n.add(s)}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function CE(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function me(e){if(!e)return null;if(Mu.has(e))return Mu.get(e);let t=null;try{let{stdout:n}=await AE("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=CE(n)}catch{t=null}return Mu.set(e,t),t}async function bo(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await cn())if((await me(n.path))?.toLowerCase()===t)return n.path;return null}var AE,sw,EE,_E,IE,RE,Mu,Un=O(()=>{AE=TE(vE),sw=process.env.HOME||"/home/globo",EE=Fn.join(process.env.BOSTRAT_STATE_DIR||Fn.join(sw,".bostrat"),"repos"),_E=[...(process.env.BOSTRAT_REPOS_ROOT||Fn.join(sw,"Documents")).split(","),EE].map(e=>e.trim()).filter(Boolean),IE=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,RE=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Mu=new Map});import ME from"node:fs";import aw from"node:fs/promises";import Ws from"node:path";import NE from"node:os";import{fileURLToPath as PE}from"node:url";import{execFile as DE}from"node:child_process";import{promisify as BE}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 Ki(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Gi(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!cw.test(n))throw ko("repo must be owner/name",400);let r=Pu.get(n);if(r&&r.expiresAt-Date.now()>FE)return r;if(!Ki(t))throw ko("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await lw(`${ke(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:uw(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw ko(`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 ko(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 Pu.set(n,a),a}async function qs({env:e=process.env,force:t=!1}={}){if(!Ki(e))return{configured:!1,repos:[],username:null};if(!t&&$r&&Date.now()-$r.at<UE)return $r.data;try{let n=await lw(`${ke(e)}/v1/github/repos`,{headers:uw(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return $r={at:Date.now(),data:r},r}catch(n){return $r?$r.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function dw(){Pu.clear(),$r=null}async function qt({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await me(e):null);if(!r)return{};let{token:o}=await Gi(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Du({env:e=process.env}={}){return(await qs({env:e})).username??null}function KE(e){let[t,n]=String(e).split("/");return Ws.join(LE,t,n)}async function Bu(e,{env:t=process.env}={}){let n=(...r)=>iw("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${WE()}" ${qE(t)}`),await n("config","credential.useHttpPath","true")}async function Vi(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!cw.test(String(e||"")))throw ko(`bad repo slug "${e}"`,400);let r=KE(e);if(ME.existsSync(Ws.join(r,".git")))return r;let{token:o}=await Gi(e,{env:t});await aw.mkdir(Ws.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await iw("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:HE,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await aw.rm(r,{recursive:!0,force:!0}).catch(()=>{}),ko(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Bu(r,{env:t}),r}var iw,jE,LE,cw,FE,UE,HE,lw,Pu,$r,uw,ko,WE,qE,Qe=O(()=>{Un();iw=BE(DE),jE=process.env.BOSTRAT_STATE_DIR||Ws.join(process.env.HOME||NE.tmpdir(),".bostrat"),LE=Ws.join(jE,"repos"),cw=/^[^/\s]+\/[^/\s]+$/,FE=5*6e4,UE=6e4,HE=10*6e4,lw=fetch,Pu=new Map,$r=null;uw=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),ko=(e,t)=>Object.assign(new Error(e),{status:t});WE=()=>PE(new URL("../bin/bostrat-git-credential",import.meta.url)),qE=(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 hw(e,t,n,r=fw){let o=ke(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await VE(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function ww(e,{env:t=process.env,force:n=!1,timeoutMs:r=fw}={}){if(!pw(t))return{facts:[],source:"unconfigured"};let o=ju.get(e);if(!n&&o&&Date.now()-o.at<GE)return{facts:o.facts,source:"cache"};try{let s=await hw(`/v1/memory/${encodeURIComponent(e)}`,{headers:mw(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 ju.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 zi(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 hw(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...mw(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 ju.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 GE,fw,ju,VE,mw,gw=O(()=>{Qe();GE=6e4,fw=5e3,ju=new Map,VE=(...e)=>fetch(...e);mw=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Lu from"node:fs/promises";import xo from"node:path";import zE from"node:os";import{EventEmitter as JE}from"node:events";import{randomUUID as YE}from"node:crypto";function So(e){return e?.cwd?xo.join(e.cwd,".bostrat","events"):xo.join(Fu,"events")}async function e_(e,t){if(!Ji.has(t)){let o=(await L(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Ji.set(t,o)}let n=Ji.get(t)+1;return Ji.set(t,n),n}function t_(e,t){let n=Array.isArray(e?.epochs)?e.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(t))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=t)return r}return 0}}async function _(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!XE.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!ZE.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!QE.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?xo.join(Fu,"events"):So(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=t_(c,Number(t.payload?.ts??t.ts)||Date.now())}let s=await e_(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||YE(),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 Lu.mkdir(r,{recursive:!0}),await Lu.appendFile(yw(r,n),JSON.stringify(i)+`
|
|
15
|
+
`),st.emit("event",i),st.emit(`track:${n}`,i),i}async function L(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?xo.join(Fu,"events"):So(e));let o;try{o=await Lu.readFile(yw(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 bw(e,t){let n=`track:${e}`;return st.on(n,t),()=>st.off(n,t)}var XE,ZE,QE,st,Fu,yw,Ji,ue=O(()=>{XE=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","memory.harvested","handoff.artifact","code.pushed","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","loop.create.offered","loop.create.answered","loop.created","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),ZE=new Set(["user","agent","orchestrator","system","peer"]),QE=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),st=new JE;st.setMaxListeners(0);Fu=process.env.BOSTRAT_STATE_DIR||xo.join(process.env.HOME||zE.tmpdir(),".bostrat");yw=(e,t)=>xo.join(e,`${t}.ndjson`),Ji=new Map});import q from"node:fs/promises";import n_ from"node:os";import oe from"node:path";import{execFile as r_}from"node:child_process";import{promisify as o_}from"node:util";function Xi(){return oe.join(Zi,"memory","repos")}function Or(e){return e===ln?oe.join(Zi,"memory",ln):oe.join(Xi(),e)}async function Gs(e){try{return JSON.parse(await q.readFile(oe.join(e,Ew),"utf8"))}catch{return{}}}async function Hu(e,t){let n=await Gs(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await q.mkdir(e,{recursive:!0}),await q.writeFile(oe.join(e,Ew),JSON.stringify(n))}function xw(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 Sw(e){try{let{stdout:t}=await s_("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Wu(e){if(!e)return[];let t=xw(await Sw(e));if(t)return[t];let n=[],r=[];try{r=await q.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=xw(await Sw(oe.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Qi(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 Hn(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function Kt(e,t=null){let n=t??await q.readFile(e,"utf8").catch(()=>""),{fm:r}=Qi(n),o=Hn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await q.stat(e)).mtimeMs}catch{return 0}}function _w(e,t){let n=Hn(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===ln){let o=Hn(e,"trust");if(!o||/^operator/i.test(o))return ln}return t}function qu(e,t,n,r=new Map){let o=(e||"").split(`
|
|
15
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
16
|
-
`)){let l=
|
|
18
|
+
`)){let l=To(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=To(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
17
19
|
`).replace(/\n{3,}/g,`
|
|
18
20
|
|
|
19
|
-
`)}function
|
|
21
|
+
`)}function Iw(e,t){let n=Hn(t,"name")||e.replace(/\.md$/,""),r=Hn(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Vs(e){let t=[];try{t=await q.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 Rw(e,t){let n=oe.join(e,".bostrat","memory"),r=await Vs(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=vw.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let f of r){let d=oe.join(n,f);if(o)try{if((await q.stat(d)).mtimeMs<o-i_)continue}catch{continue}let p;try{p=await q.readFile(d,"utf8")}catch{continue}let{fm:m}=Qi(p),h=_w(m,t);if(!h)continue;let g=Or(h),x=oe.join(g,f),k=await Kt(d,p);if(l.has(g)||l.set(g,await Gs(g)),(l.get(g)[f]||0)>=k)continue;let A=await Kt(x).catch(()=>0);if(await q.access(x).then(()=>!0).catch(()=>!1)&&A>=k)continue;await q.mkdir(g,{recursive:!0}),await q.writeFile(x,p),c++;let M=a.get(g);M||a.set(g,M={won:new Set,generated:new Map}),M.won.add(f),M.generated.set(f,Iw(f,m)),i.has(h)||i.set(h,new Set),i.get(h).add(f)}let u=await q.readFile(oe.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[f,d]of a){let p=oe.join(f,"MEMORY.md"),m=await q.readFile(p,"utf8").catch(()=>"");await q.writeFile(p,qu(m,u,d.won,d.generated))}return vw.set(e,s),{harvested:c,changedByKey:i}}async function $w(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await q.access(oe.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await Wu(r),a=await Rw(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 c_(){let e=oe.join(Zi,"workspaces"),t=[];try{t=await q.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return $w(t.filter(n=>n.isDirectory()).map(n=>oe.join(e,n.name)))}async function d_(e,t){let n=Or(e),r=await Gs(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!l_.test(l)||l==="MEMORY.md")continue;let u=oe.join(n,l),f=await q.access(u).then(()=>!0).catch(()=>!1),d=f?await Kt(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await q.rm(u,{force:!0}),await Hu(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await q.mkdir(n,{recursive:!0}),await q.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=oe.join(n,"MEMORY.md"),l=await q.readFile(c,"utf8").catch(()=>""),u=qu(l,[...s.values()].join(`
|
|
20
22
|
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
21
|
-
`).filter(d=>!f.has(
|
|
22
|
-
`)}await
|
|
23
|
-
`);t.length>
|
|
24
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
25
|
-
`);return n}async function
|
|
23
|
+
`).filter(d=>!f.has(To(d))).join(`
|
|
24
|
+
`)}await q.mkdir(n,{recursive:!0}),await q.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function ec(e,t=null){let n=Or(e),r=t?.length?t:await Vs(n);if(!r.length)return{pushed:0,source:"empty"};let o=u_(await q.readFile(oe.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=oe.join(n,a),c;try{c=await q.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Kt(i,c),indexLine:o.get(a)??null})}return zi(e,s)}async function f_(e,{timeoutMs:t}={}){let{facts:n,source:r}=await ww(e,t?{timeoutMs:t}:{});n.length?await d_(e,n):r==="plane"&&await ec(e)}function p_(e){let t=e.split(`
|
|
25
|
+
`);t.length>kw&&(t=t.slice(0,kw));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>a_&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function h_(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=oe.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let f=Or(u),d=await Gs(f);for(let[g,x]of Object.entries(d)){let k=oe.join(n,g);await q.access(k).then(()=>!0).catch(()=>!1)&&await Kt(k)<=x&&(await q.rm(k,{force:!0}),o++)}let p=await Vs(f);if(!p.length)continue;await q.mkdir(n,{recursive:!0});for(let g of p){let x=oe.join(f,g),k=oe.join(n,g),A=await Kt(x),R=await Kt(k).catch(()=>0);await q.access(k).then(()=>!0).catch(()=>!1)&&R>=A||(await q.copyFile(x,k),r++)}let m=await q.readFile(oe.join(f,"MEMORY.md"),"utf8").catch(()=>""),h=u===ln?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(t.length>1?`${h}
|
|
26
28
|
|
|
27
|
-
${
|
|
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(`
|
|
28
30
|
|
|
29
31
|
`)+`
|
|
30
|
-
`,c=
|
|
31
|
-
`).filter(
|
|
32
|
-
`);await
|
|
33
|
-
`).filter(l=>
|
|
34
|
-
`)).catch(()=>{});let c=await
|
|
35
|
-
`)){let r=
|
|
32
|
+
`,c=p_(i);await q.mkdir(n,{recursive:!0}),await q.writeFile(oe.join(n,"MEMORY.md"),c);let l=oe.join(n,"claude");return await q.mkdir(l,{recursive:!0}),await q.writeFile(oe.join(l,"MEMORY.md"),m_,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Ow(e){try{let t=await Wu(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,ln];return await Promise.all(n.map(r=>f_(r,{timeoutMs:w_}))),await h_(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Cw(){return{...De,pending:vo.size,queued:Uu.size}}function Mw(e){if(Yi.has(e))return Yi.get(e);Uu.add(e);let t=Tw.then(async()=>{Uu.delete(e),Yi.delete(e);let n=await Gu(e);return De.runs++,De.lastRunAt=Date.now(),n.error?(De.errors++,De.lastError=n.error):De.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Yi.set(e,t),Tw=t.then(()=>{}),t}function tc(e,{delayMs:t=g_}={}){if(!e||typeof e!="string")return;De.scheduled++;let n=vo.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{vo.delete(e),Mw(e)},t);r.unref?.(),vo.set(e,r)}async function Ku(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await q.access(oe.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=vo.get(e);r&&(clearTimeout(r),vo.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([Mw(e),s])}finally{clearTimeout(o)}}async function Nw(e){return Ks||(Ks=(async()=>{try{Aw||(Aw=!0,await b_());let{harvested:t,changedByKey:n}=await $w(e);for(let[r,o]of n)await ec(r,[...o]);return De.reconciles++,De.lastReconcileAt=Date.now(),De.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return De.errors++,De.lastError=t?.message||String(t),{harvested:0,error:De.lastError}}finally{Ks=null}})(),Ks)}function y_(){return oe.join(Zi,"memory",".full-sweep-done")}async function Pw(){let e=y_();try{let t=JSON.parse(await q.readFile(e,"utf8"));return De.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...De.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await c_();for(let[o,s]of n)await ec(o,[...s]);let r={at:Date.now(),harvested:t};return await q.mkdir(oe.dirname(e),{recursive:!0}),await q.writeFile(e,JSON.stringify(r)),De.backfill=r,{ran:!0,...r}}catch(t){return De.errors++,De.lastError=t?.message||String(t),{ran:!1,error:De.lastError}}}async function b_(){let e=0,t=[];try{t=await q.readdir(Xi(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=oe.join(Xi(),n.name),o=Or(ln),s=new Set,a=new Map;for(let p of await Vs(r)){let m=oe.join(r,p),h;try{h=await q.readFile(m,"utf8")}catch{continue}let{fm:g}=Qi(h);if(_w(g,n.name)!==ln)continue;let x=oe.join(o,p),k=await Kt(x).catch(()=>0),A=await Kt(m,h);await q.mkdir(o,{recursive:!0}),A>k&&await q.writeFile(x,h),await q.rm(m,{force:!0}),s.add(p),a.set(p,Iw(p,g)),e++}if(!s.size)continue;let i=Date.now();await Hu(r,[...s].map(p=>[p,i])),await zi(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=oe.join(r,"MEMORY.md"),l=await q.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(p=>!s.has(To(p))).join(`
|
|
34
|
+
`);await q.writeFile(c,u).catch(()=>{});let f=oe.join(o,"MEMORY.md"),d=await q.readFile(f,"utf8").catch(()=>"");await q.writeFile(f,qu(d,l,s,a))}return{moved:e}}async function Dw(){let e=[],t=[ln];try{for(let n of await q.readdir(Xi(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=Or(n),o=await Vs(r),s=await Gs(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await q.readFile(oe.join(r,i),"utf8").catch(()=>""),{fm:l}=Qi(c);a.push({file:i,name:Hn(l,"name")||i.replace(/\.md$/,""),description:Hn(l,"description")||"",scope:Hn(l,"scope")||null,modified:await Kt(oe.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function Bw(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=Or(e),r=oe.join(n,t);if(!await q.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await q.rm(r,{force:!0}),await Hu(n,[[t,s]]);let a=oe.join(n,"MEMORY.md"),i=await q.readFile(a,"utf8").catch(()=>"");await q.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>To(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await zi(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Gu(e){try{let t=await Wu(e),{harvested:n,changedByKey:r}=await Rw(e,t[0]||null);for(let[o,s]of r)await ec(o,[...s]);return n>0&&await x_(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function x_(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,k_)}}await _({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var s_,Zi,kw,a_,ln,Ew,To,vw,i_,l_,u_,m_,w_,g_,vo,Uu,Tw,De,Yi,Aw,Ks,k_,zs=O(()=>{gw();ue();s_=o_(r_),Zi=process.env.BOSTRAT_STATE_DIR||oe.join(process.env.HOME||n_.tmpdir(),".bostrat"),kw=200,a_=25*1024;ln="operator";Ew=".tombstones.json";To=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;vw=new Map,i_=2e3;l_=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,u_=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=To(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};m_=`# Track memory
|
|
36
38
|
|
|
37
39
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
38
40
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
39
41
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
40
|
-
`;
|
|
41
|
-
`,n!=null?{mode:n}:void 0),await
|
|
42
|
-
`,"utf8");let n=(await
|
|
43
|
-
`).filter(Boolean);n.length>
|
|
42
|
+
`;w_=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;g_=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,vo=new Map,Uu=new Set,Tw=Promise.resolve(),De={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Yi=new Map;Aw=!1,Ks=null;k_=20});import Vu from"node:fs/promises";import S_ from"node:path";async function at(e,t,{mode:n}={}){await Vu.mkdir(S_.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++v_}.tmp`;await Vu.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await Vu.rename(r,e)}var v_,Wn=O(()=>{v_=0});import Hw from"node:fs/promises";import Ww from"node:os";import qw from"node:path";function Ae(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Xs(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),T_);try{return await $_(`${ke(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function Gw(){return rc||(zu||=Hw.readFile(Kw(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>rc={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>zu=null),zu)}async function oc(){await at(Kw(),rc).catch(()=>{})}function Fw(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Yu(e){let t=await Gw(),n=Fw(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=>Fw(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>jw&&(t.ops=t.ops.slice(-jw)),await oc()}function Uw(){nc||(nc=setTimeout(()=>{nc=null,Ao().catch(()=>{})},O_),nc.unref?.())}function Ju(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function M_(e,t=process.env){if(e.op==="record"){let n=await Xs(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Ys(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:R_()}]})},t);if(!n.ok)throw Ju(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 Hw.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Xs(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Ys(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Ju(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 Xs(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Ys(t)},t);if(!n.ok)throw Ju(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function Ao({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Ae(e)?Js||(Js=(async()=>{let r=await Gw();N_(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await M_(a,e)}catch(c){if(!c?.refused)return Uw(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<E_)return Uw(),await oc(),{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>Lw&&(r.parked=r.parked.slice(-Lw)),s++,await oc();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await oc()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Js=null}),Js):{flushed:0,source:"unconfigured"}}function N_(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=C_);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 et(e,t,{trackId:n=null,env:r=process.env}={}){return Ae(r)?(await Yu({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),Ao({env:r})):{queued:!1,source:"unconfigured"}}async function qn(e,t,n,r,o,{env:s=process.env}={}){return Ae(s)?(await Yu({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),Ao({env:s})):{queued:!1,source:"unconfigured"}}async function Rt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Ae(r)?(await Yu({op:"delete",family:e,id:t,deletedAt:n}),Ao({env:r})):{queued:!1,source:"unconfigured"}}async function je(e,{env:t=process.env}={}){if(!Ae(t))return{records:[],source:"unconfigured"};try{let n=await Xs(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Ys(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 Zs(e,t,n,r,{env:o=process.env}={}){if(!Ae(o))return null;try{let s=await Xs(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ys(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 T_,A_,jw,E_,Lw,__,I_,Kw,R_,$_,O_,C_,Ys,rc,zu,nc,Js,Kn=O(()=>{Qe();Wn();T_=1e4,A_=3e4,jw=2e3,E_=5,Lw=100,__=60*6e4,I_=()=>process.env.BOSTRAT_STATE_DIR||qw.join(process.env.HOME||Ww.tmpdir(),".bostrat"),Kw=()=>qw.join(I_(),"content-outbox.json"),R_=()=>process.env.BOSTRAT_NODE_ID||Ww.hostname(),$_=(...e)=>fetch(...e),O_=A_,C_=__;Ys=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});rc=null,zu=null,nc=null,Js=null});import Xu from"node:fs/promises";import Vw from"node:path";async function zw(e,t,{box:n=null,agent:r=null}={}){if(!e||!Ae())return{pushed:!1};try{let o=Date.now();return await et("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await qn("checkpoints",e,o,"md",t),Qs=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function L_(){if(Qs&&Date.now()-Qs.at<j_)return{records:Qs.records,source:"plane"};let e=await je("checkpoints");return e.source==="plane"&&(Qs={at:Date.now(),records:e.records}),e}async function sc(e){try{let t=B_(e);if(!t||!Ae())return{restored:!1};let n=Vw.join(e,P_,D_);if(await Xu.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await L_();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 Zs("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Xu.mkdir(Vw.dirname(n),{recursive:!0}),await Xu.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var P_,D_,B_,j_,Qs,ac=O(()=>{Kn();P_=".bostrat/handoff",D_="latest.md",B_=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;j_=6e4,Qs=null});import ea from"node:fs/promises";import F_ from"node:os";import Yw from"node:path";function Cr(e){let t=String(e??"");for(let n of U_)t=t.replace(n,"\u2039redacted\u203A");return t}function ic(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function H_(e){try{await ea.mkdir(Zu,{recursive:!0}),await ea.appendFile(Eo,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await ea.readFile(Eo,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>Jw&&await ea.writeFile(Eo,`${n.slice(-Jw).join(`
|
|
44
46
|
`)}
|
|
45
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${
|
|
46
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=
|
|
47
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,m]=r.split("|"),
|
|
48
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
49
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
50
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function
|
|
51
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function
|
|
52
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
53
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function
|
|
54
|
-
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(
|
|
55
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function
|
|
56
|
-
`)){let
|
|
57
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function
|
|
58
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let f=[];if(u.type==="function_call"&&
|
|
59
|
-
`).trim():"";
|
|
60
|
-
`).trim():""}function
|
|
61
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let
|
|
62
|
-
|
|
63
|
-
`):""}var
|
|
64
|
-
`);if(!
|
|
65
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
66
|
-
`);return t}function
|
|
67
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
68
|
-
`).pop()||"";return
|
|
69
|
-
`),o=r.findIndex(i=>
|
|
70
|
-
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function
|
|
71
|
-
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function
|
|
72
|
-
`))
|
|
73
|
-
`).slice(-
|
|
74
|
-
`);return FI.test(n)}function aa(e,t){return hd(e,t)==="busy"}function fc(e,t){return hd(e,t)!=="idle"}function pc(e,t){let n=Ue[e];return n?.up?n.up.test(t):!0}function HI(e,t){let n=Ue[e];return n?wd(e,t)?!1:n.up.test(t)&&hd(e,t)==="idle":!0}function wd(e,t){let n=Ue[e];return n?n.auth?.test(t)&&!n.up.test(t)?!0:n.block?.test(t)?e==="codex"?!0:!n.up.test(t):!1:!1}async function Ig(e,t,n,{bailOnDialog:r=!1}={}){let o=Date.now()+n,s=0;for(await Rr(e).catch(()=>{});;){let a=await Se(e);if(HI(t,a)){await Un(sa);return}if(r&&t==="claude"&&MI.test(a)&&!Ue.claude.up.test(a)){Y({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="cursor"&&CI.test(a)&&!Ue.cursor.up.test(a)&&s<3){if(!_g(e,"cursor-trust")){await Un(pd);continue}s+=1,await Y({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s}).catch(()=>{}),await Fe(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Un(sa);continue}if(t==="codex"&&s<3){let i=PI(a);if(i){if(!_g(e,`codex-update:${i.from}->${i.to}`)){await Un(pd);continue}s+=1,await Y({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s,update:`${i.from} -> ${i.to}`}).catch(()=>{}),await Fe(e,"3",{enter:!1,kind:"codex-update-skip"}),kg({sessionName:e,from:i.from,to:i.to}),await Un(sa);continue}}if(Date.now()>o){if(wd(t,a)){let i=`${t} is blocked on an interactive prompt`;throw t==="codex"?i=Ue.codex.auth.test(a)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":t==="cursor"&&Ue.cursor.auth.test(a)?i="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":t==="claude"&&Ue.claude.auth.test(a)?i="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":t==="claude"&&Ue.claude.block.test(a)&&(i='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),Y({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}if(await To(e)){let i=`${t} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw Y({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),Y({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,paneBlocked:!!Ue[t]?.block?.test(a),paneUp:!!Ue[t]?.up?.test(a),paneBusy:!!Ue[t]?.busy?.test(a)}).catch(()=>{}),await Un(sa);return}await Un(pd)}}function $g({agent:e,agentCmd:t}={}){let n=t?Dn(t):Dn(e||"");return{label:n,realClaude:t?Ir(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 ia({cwd:e,sessionName:t,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=$g({agent:n,agentCmd:r});return!t||!o&&!s?null:Hn({cwd:e,sessionName:t,agent:n,agentCmd:r}).catch(()=>{})}async function Hn({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=OI(),bailOnDialog:a=!1}={}){let{label:i,realClaude:c,realCodex:l,realCursor:u}=$g({agent:r,agentCmd:o});if(c&&e&&await On(e),l&&e&&await po(e),u&&e&&await mo(e),(c||l||u)&&n){await Ig(n,i,s,{bailOnDialog:a});return}let f=Ue[r]?r:Ue[i]?i:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f){await Ig(n,f,s);return}await Un(sa)}var OI,pd,sa,Ue,CI,MI,NI,BI,dc,DI,jI,FI,UI,mc=C(()=>{Xe();js();Ni();Pi();lc();uc();nn();OI=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,pd=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,sa=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Ue={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key|Successfully logged in\.[\s\S]*Press Enter to continue\./},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},CI=/Trust this workspace|\[a\]\s*Trust/,MI=/Resume from summary/i,NI=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;BI=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,dc=new Map;DI=68,jI=/^\s*[─━]{10,}\s*$/;FI=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,UI=15});function jt(e){return e==="codex"?{enter:!0,submitDelayMs:WI}:e==="claude"?{enter:!0,submitDelayMs:qI}:{enter:!0}}var WI,qI,ca=C(()=>{WI=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),qI=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})()});function Nr(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of KI){let o=0;for(let s of GI[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Og(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))VI.has(r)&&(t+=o||0),zI.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function la(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 KI,GI,VI,zI,gd=C(()=>{KI=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],GI={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/]};VI=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),zI=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Pg(e){let t=la(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||JI.has(r)||n.push(r);return n}function YI(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(Ng(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Ng(n,"assistant",o))}}return t}function Ng(e,t,n){let r=Nr(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 ua(e){let t=YI(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Pg(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function hc(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(Pg(t))];if(!i.length)return[];let c=new Map;for(let u of i){let f=e.postings.get(u);if(!f)continue;let d=f.size,m=Math.log(1+(e.N-d+.5)/(d+.5));for(let[p,w]of f){let y=e.lengths[p],b=w+Cg*(1-Mg+Mg*(y/(e.avgdl||1))),v=m*(w*(Cg+1)/b);c.set(p,(c.get(p)||0)+v)}}let l=[];for(let[u,f]of c){let d=e.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var Cg,Mg,JI,yd=C(()=>{gd();Cg=1.5,Mg=.75,JI=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 wc from"node:fs/promises";import Bg from"node:path";async function XI(e){let t=Bg.join(e,".bostrat","events"),n;try{n=await wc.readdir(t)}catch{return ua([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await wc.readFile(Bg.join(t,o),"utf8");for(let a of s.split(`
|
|
75
|
-
`)
|
|
76
|
-
${o}`.trim()||t.slice(0,2e3)}function QI(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=hc(e,t,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${Dg}, not a summary)`,"_Auto-selected from the Track's event log by lexical relevance to the Objective / Next steps. Verbatim excerpts, not re-summarization (docs/memory-segmentation-indexing-scope.md \xA71.3)._","",...a,""].join(`
|
|
77
|
-
`)
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Eo}: ${t?.message||t}`)}}async function Y(e={}){let t={at:new Date().toISOString(),ts:Date.now(),...e},n=[t.flow||"handoff",t.phase||t.step||"?",t.trackId?`track=${t.trackId}`:"",t.sessionName?`box=${t.sessionName}`:"",t.error?`ERR=${t.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),t.sessionName&&ic())try{t.paneTail=Cr((await Ee(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(ic()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(t.phase||t.step||"")))&&await H_(t)}async function Xw({limit:e=80}={}){try{return(await ea.readFile(Eo,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Cr(o.paneTail)),o}catch{return{raw:Cr(r)}}})}catch{return[]}}var U_,Zu,Eo,Jw,Zw,un=O(()=>{tt();U_=[/[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];Zu=process.env.BOSTRAT_DEBUG_DIR||Yw.join(F_.homedir(),".bostrat","debug"),Eo=Yw.join(Zu,"handoff.jsonl"),Jw=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;Zw={DEBUG_DIR:Zu,LOG_FILE:Eo}});import{execFile as W_}from"node:child_process";import{promisify as q_}from"node:util";import lc from"node:fs";import{randomUUID as K_}from"node:crypto";function Ro(e){return new Promise(t=>setTimeout(t,e))}function cc(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 eg(e){return`/tmp/tmux-${e}/default`}function G_({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=eg(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function na(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${ta}=1`]}function nd(e,t,n){let r=Mr(e),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(e);if(!r||o)return e;let s=`${e} --session-id ${t}`,a=/(^|\s)(--name|-n)(\s|=)/.test(e);return n&&!a&&_e(n)&&(s+=` --name ${n}`),s}function Mr(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Gn(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function J_(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Mr(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function tg(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await ng(e,{loginShell:t});return r===uc?!0:r===_o?!1:n}async function ng(e,{loginShell:t=!1,timeoutMs:n=Qu}={}){if(!e)return _o;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await be(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),uc}catch(s){return s?.killed||s?.signal?Io:s?.code===1||s?.code===127?_o:Io}}async function dc(e,{retries:t=Y_,sleep:n=X_,probe:r=ng}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Qu});for(let a=0;s===Io&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Qu*2});return s}function K(...e){return["-S",ft,...e]}async function Vn(){if(!lc.existsSync(ft))return!1;try{return await be(U,K("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function ra(){if(await Vn())return!0;try{lc.mkdirSync(ft.replace(/\/[^/]+$/,""),{recursive:!0}),await be(U,K("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Vn()}function _e(e){return typeof e=="string"&&Z_.test(e)}async function $t(){if(!lc.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 be(U,K("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
49
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,p,m]=r.split("|"),h=Number(i),g=Number(m);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(h)?n-h*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!z_.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function se(e){return(await $t()).find(n=>n.name===e)||null}function tI(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=eI&&Q_.test(t)}function fc(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function rg(e){try{let{stdout:t}=await be(U,K("list-sessions","-F","#{session_name}"));return t.split(`
|
|
50
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function oI(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 sI(e,t){let n=Date.now()+oa(),r=oI(t),o=!1,s=0;for(;;){if(await Ro(nI),s+=1,!await rg(e))return;let a=await Ee(e,{lines:15}),i=!o&&rI.test(a)&&!!r?.test(a);if(!i&&s>1&&!await dn(e))return;if(i){o=!0,await Y({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Gn(t),promptHead:t.slice(0,120)}).catch(()=>{}),await be(U,K("send-keys","-t",e,"C-u")),await be(U,K("send-keys","-t",e,"-l",t)),await be(U,K("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await Y({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Gn(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function zn(e,t=V_,n={}){if(!tI(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=fc(r);if(!_e(o))throw new Error("resulting tmux session name is invalid");if(!await ra())throw new Error(`host tmux server not running on ${ft} and auto-start failed \u2014 start one: ${U} -S ${ft} new-session -d -s keeper "sleep 2147483647"`);if(await rg(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||ed,a=n.agent||Gn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||K_(),l=i?s:nd(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&Mr(s),d=n.firstRun===!0;d||(f&&await Ln(t),a==="codex"&&await go(t),a==="cursor"&&await yo(t),await Ow(t),n.freshWorkspace!==!0&&await sc(t));let p=f&&!d?Ui():null,m=Qh(),h=["-e",`${Li}=${m}`];p&&h.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&h.push(...na());try{await be(U,K("new-session","-d","-s",o,"-c",t,...h))}catch(g){throw cc(p?cc(g,p):g,m)}return await Nr(o),await be(U,K("set-option","-t",o,"@bostrat-session","1")),await be(U,K("set-option","-t",o,"@bostrat-title",r)),await be(U,K("set-option","-t",o,"@bostrat-sid",c)),await be(U,K("set-option","-t",o,"@bostrat-agent",a)),await be(U,K("send-keys","-t",o,l,"Enter")),J_(l)&&sI(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:m}}async function Ot(e){if(!_e(e))throw new Error("invalid session name");await be(U,K("kill-session","-t",e))}async function og(e,t){if(_e(e))try{let n=t?K("set-option","-t",e,"@bostrat-sid",t):K("set-option","-u","-t",e,"@bostrat-sid");await be(U,n)}catch{}}async function Ee(e,{lines:t=40}={}){if(!_e(e))return"";try{let n=K("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await be(U,n);return String(r||"")}catch{return""}}async function sg(e){if(!_e(e))return 0;try{let{stdout:t}=await be(U,K("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
51
|
+
`).filter(Boolean).length}catch{return 0}}async function Nr(e,{cols:t=cI,rows:n=lI}={}){if(!_e(e))return!0;if(await sg(e))return!1;try{let r=await ag(e);if(r.cols===t&&r.rows===n)return!0;await be(U,K("resize-window","-t",e,"-x",String(t),"-y",String(n))),await be(U,K("set-window-option","-t",e,"window-size","latest")),await Ro(80)}catch{}return!0}async function uI(e){if(!_e(e))return 0;try{let{stdout:t}=await be(U,K("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function dn(e){let t=await uI(e);if(!t)return!1;try{return await be("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function ag(e){if(!_e(e))return{cols:0,rows:0};try{let{stdout:t}=await be(U,K("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 pc(e,{cols:t=aI,rows:n=iI}={}){if(_e(e)&&!await sg(e))try{let r=await ag(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await be(U,K("resize-window","-t",e,"-x",String(o),"-y",String(s))),await be(U,K("set-window-option","-t",e,"window-size","latest")),await Ro(80))}catch{}}async function fn(e){return await pc(e),Ee(e,{lines:Le})}async function $e(e,t,{settleMs:n=40,kind:r}={}){if(!_e(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>dI.has(s));for(let s=0;s<o.length;s++)await be(U,K("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Ro(n);return Y({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function $o(e,t,{kind:n}={}){if(!_e(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await be(U,K("send-keys","-t",e,"-N",String(r),"BSpace")),Y({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function hc(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!_e(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await be(U,K("send-keys","-t",e,"-l",s)),n>0&&await Ro(n);Y({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}async function Fe(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!_e(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&await be(U,K("send-keys","-t",e,"-l",t)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await Ro(s),await be(U,K("send-keys","-t",e,"Enter"))}Y({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}var be,U,Qw,ft,V_,ed,ta,td,z_,uc,_o,Io,Qu,Y_,X_,rd,Z_,Q_,eI,oa,nI,rI,aI,iI,Le,cI,lI,dI,mc,tt=O(()=>{Us();Wi();qi();zs();ac();un();Fs();be=q_(W_);U=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";Qw=typeof process.getuid=="function"?process.getuid():0,ft=G_({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return lc.existsSync(eg(Qw))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Qw}),V_=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",ed=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",ta="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";td="bostrat-usage-probe",z_=new Set([td]);uc="present",_o="absent",Io="unknown",Qu=5e3,Y_=[250,1e3,3e3],X_=e=>new Promise(t=>setTimeout(t,e));rd={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"};Z_=/^[A-Za-z0-9_.-]{1,64}$/;Q_=/^[A-Za-z0-9 ._↻-]+$/,eI=64;oa=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,nI=1e3,rI=/not found|Unknown command/i;aI=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,iI=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Le=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,cI=Number(process.env.BOSTRAT_BOX_COLS)||120,lI=Number(process.env.BOSTRAT_BOX_ROWS)||40;dI=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);mc=1600});import sa from"node:fs";import gI from"node:fs/promises";import Oo from"node:path";import yI from"node:os";function ad(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function id(e){let t=Oo.join(sd,ad(e)),n;try{n=sa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Oo.join(t,o);try{let{mtimeMs:a}=sa.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Ct(e,t){if(!t)return null;let n=Oo.join(sd,ad(e),`${t}.jsonl`);return sa.existsSync(n)?n:null}function pg(e){if(!e)return[];let t=Oo.join(sd,ad(e)),n;try{n=sa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=sa.statSync(Oo.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function it(e,t=131072){let n=await gI.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 kI(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 aa(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=Ct(e,t),o=!0,!r)return null}else if(n){if(r=id(e),o=!1,!r)return null}else return null;let s;try{s=kI(await it(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(fg,s.model),i=fg[s.model]||bI;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 fg,bI,sd,Mt=O(()=>{fg={"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},bI=2e5,sd=process.env.BOSTRAT_CLAUDE_PROJECTS||Oo.join(yI.homedir(),".claude","projects")});function xI(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 ia(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 ca(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(wg(o)){t.has(o)&&r++;continue}n.push(o)}return n}function gg(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 TI(e){let t=gg(e),n=Ge(xe(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${Ie(n)}`:`Read lines ${t.from}\u2013${t.to} of ${Ie(n)}`:e.includes("-i")&&n?`Edit ${Ie(n)}`:null}function cd(e){if(e.includes("--files")){let s=Ge(xe(e,mg)[0]);return s&&s!=="."?`List files in ${Co(s)}`:"List files"}let t=xe(e,mg),n=t[0];if(!n)return null;let r=Ge(t.slice(1).find(s=>s!==".")),o=Mo(n,40);return r?`Search for \u201C${o}\u201D in ${Co(r)}`:`Search for \u201C${o}\u201D`}function ld(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 ud(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 ${Ie(n)}`:null}function AI(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 hg(e){return e.find(t=>/^https?:\/\//.test(t))||null}function II(e){let t=ca(ia(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Ie(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return dd(t[2]);let n=Ie(t[0]);if(_I.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=EI[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function dd(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=xI(t);for(let i=0;i<r.length-1;i++){let c=ca(ia(r[i])),l=ca(ia(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Ie(c[0]))&&Ie(l[0])==="sed"){let u=gg(l),f=xe(c)[0];if(u&&f)return Mo(u.from===u.to?`Read line ${u.from} of ${Ie(f)}`:`Read lines ${u.from}\u2013${u.to} of ${Ie(f)}`,88)}}let o=[];for(let i of r){let c=II(i);c&&o.push({d:c,head:Ie(ca(ia(i))[0]||""),sub:xe(ca(ia(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":Mo(s?s.d:o[0].d,88)}var Mo,Ge,Ie,Co,wg,mg,SI,vI,EI,_I,yg=O(()=>{Mo=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,Ge=e=>e&&!/[$`]/.test(e)?e:null,Ie=e=>e.replace(/\/+$/,"").split("/").pop()||e,Co=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},wg=e=>e.startsWith("-"),mg=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);SI={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 ${Ie(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"},vI={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"};EI={git:e=>{let t=xe(e,new Set(["-C","-c"])),n=SI[t[0]];return n?n(t,e):null},gh:e=>{let t=xe(e),n=vI[t[0]];return n?n(t):null},sed:TI,rg:cd,grep:cd,egrep:cd,nl:e=>{let t=Ge(xe(e)[0]);return t?`Read ${Ie(t)}`:null},cat:e=>{let t=Ge(xe(e)[0]);return t?`Read ${Ie(t)}`:null},head:e=>{let t=Ge(xe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Ie(t)}`:null},tail:e=>{let t=Ge(xe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Ie(t)}`:null},wc:e=>{let t=Ge(xe(e)[0]);return t?`Count lines in ${Ie(t)}`:"Count input lines"},ls:e=>{let t=Ge(xe(e)[0]);return t&&t!=="."?`List files in ${Co(t)}`:"List files"},find:e=>Ge(e[1])&&!wg(e[1])&&e[1]!=="."?`Find files in ${Co(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=Ge(xe(e)[0]);return t?`Create directory ${Co(t)}`:null},touch:e=>{let t=Ge(xe(e)[0]);return t?`Create ${Ie(t)}`:null},rm:e=>{let t=Ge(xe(e)[0]);return t?`Delete ${Co(t)}`:null},mv:e=>{let t=Ge(xe(e)[0]);return t?`Move ${Ie(t)}`:null},cp:e=>{let t=Ge(xe(e)[0]);return t?`Copy ${Ie(t)}`:null},chmod:e=>{let t=Ge(xe(e)[1]);return t?`Change permissions on ${Ie(t)}`:null},diff:e=>{let t=xe(e);return t.length>=2?`Compare ${Ie(t[0])} and ${Ie(t[1])}`:null},curl:e=>{let t=hg(e);return t?`Fetch ${Mo(t,56)}`:"Fetch a URL"},wget:e=>{let t=hg(e);return t?`Fetch ${Mo(t,56)}`:"Fetch a URL"},npm:ld,pnpm:ld,yarn:ld,npx:e=>{let t=xe(e)[0];return t?`Run ${t}`:null},node:e=>ud(e,"Node"),python:e=>ud(e,"Python"),python3:e=>ud(e,"Python"),make:e=>{let t=xe(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:AI,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}},_I=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function No(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 Jn(e,t=""){let n=t.trim();return n||dd(e)||No(e)}function fd(e,t){if(e){if(Array.isArray(e)){for(let n of e)fd(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&pd.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))fd(n,t)}}}async function bg(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await it(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(fd(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:Jn(f.command,f.description),ts:u})}return o.slice(-n)}function kg(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,h)=>h>=s&&(m.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:p,ts:Math.max(...p.map(m=>m.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var pd,Po=O(()=>{Mt();yg();pd=new Set(["Bash"])});import xg from"node:fs";import hd from"node:fs/promises";import md from"node:path";import RI from"node:os";async function Sg(e){try{return await hd.realpath(e)}catch{return e}}function OI(e=$I){let t=[],n=r=>{let o;try{o=xg.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=md.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:xg.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function CI(e,t=1048576){let n;try{n=await hd.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 MI(e){let t=await CI(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 Pr(e,t){if(!e&&!t)return null;let n=OI();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Sg(e);for(let o of n){let s=await MI(o.file);if(s&&(s===e||await Sg(s)===r))return o}return null}async function vg(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Pr(e,t);if(!o)return[];let s;try{s=await it(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
58
|
+
`)){let p=d.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let k=m.payload?.type;k==="task_started"?(l=!0,u=!1,f=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:Dr,sourceId:m.payload?.turn_id||f||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let h=m.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,wd.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let g=PI(h.content),x=c.length?c.slice():null;c=[],!(!g&&!x)&&a.push({role:"assistant",text:g,tools:x||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}function DI(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 BI(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 jI(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=BI(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Tg(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Pr(e,t);if(!o)return[];let s;try{s=await it(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 f=[];if(u.type==="function_call"&&la.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=DI(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=jI(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,m)=>{a.push({id:d?m?`${d}:${m}`:d:null,tool:u.name,command:p,description:"",summary:Jn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Ag(e,t){let n=await Pr(e,t);if(!n)return null;try{return Math.round((await hd.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var $I,NI,PI,la,wd,Dr,Br=O(()=>{Mt();Po();$I=process.env.BOSTRAT_CODEX_SESSIONS||md.join(process.env.CODEX_HOME||md.join(RI.homedir(),".codex"),"sessions");NI=new Set(["output_text","text"]),PI=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&NI.has(t.type)).map(t=>t.text).join(`
|
|
61
|
+
`).trim():"";la=new Set(["exec_command","shell","exec"]),wd=new Set(["wait","write_stdin"]),Dr="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 ua({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Eg.has(r))return Eg.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 _g(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||ua({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===Dr?"other":ua({text:t}),code:null,status:null,requestId:null}}return null}function Ig(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:ua({text:n}),code:null,status:LI(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function LI(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var n2,Eg,gc=O(()=>{Br();n2=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Eg=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 FI(e,t){return!e||!t?null:Ct(e,t)}function UI(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 HI(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 WI(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function gd(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 Rg(e,t){if(!e.uuid)return null;let n=gd(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 yd(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await it(e,t)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
63
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let h=c.attachment;h?.type==="queued_command"&&h.commandMode==="task-notification"&&typeof h.prompt=="string"&&h.prompt.trim()&&o.push({role:"user",text:h.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let h of l.content){let g=h?.type==="tool_use"?h.input?.file_path||h.input?.path:null;typeof g=="string"&&qI.test(g)&&s.set(h.id,g.split("/").pop())}if(WI(l.content)){let h=Rg(c,s);h&&o.push({role:"assistant",text:"",images:h,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=UI(l.content),d=u==="assistant"?HI(l.content):null,p=u==="user"?Rg(c,s):null;if(!f&&!d&&!p)continue;let m=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:ua({error:c.error,status:c.apiErrorStatus,text:f})}:null;o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:m||void 0})}return o.slice(-n)}async function $g(e,t,{turns:n=16}={}){let r=FI(e,t);if(!r)return"";let o=await yd(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}:** ${KI(s.text||c,700)} ${i}`.trim()}).join(`
|
|
64
|
+
|
|
65
|
+
`):""}var qI,KI,yc=O(()=>{gc();Mt();qI=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;KI=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import GI from"node:fs/promises";import{execFile as VI}from"node:child_process";import{promisify as zI}from"node:util";function Ng(e){kd=typeof e=="function"?e:null}function xd(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Pg({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Og.has(r)||(Og.add(r),Cg=Cg.then(()=>YI({sessionName:e,from:t,to:r})).catch(()=>{}))}async function YI({sessionName:e,from:t,to:n}){await Y({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await bd("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await GI.realpath(r),s=xd(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await bd("npm",["install","-g","@openai/codex"],{env:a,timeout:JI});let i=(await bd(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await Y({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await Mg({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await Y({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await Mg({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function Mg(e){if(kd)try{await kd(e)}catch{}}var bd,JI,Og,Cg,kd,bc=O(()=>{un();bd=zI(VI),JI=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Og=new Set,Cg=Promise.resolve(),kd=null});function ZI(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
66
|
+
`);if(!Dg.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(XI);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"&&Dg.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 jr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
67
|
+
`);for(let r=n.length-1;r>=0;r--)if(Lg.test(n[r]))return n.slice(r).join(`
|
|
68
|
+
`);return t}function QI(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 eR(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 tR(e,t=""){let n=jr(e);if(da.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(Bg);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let p=r[d].trim();if(p){if(Bg.test(r[d])||/^Press enter/.test(p))break;f=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!Lg.test(r[l])&&QI(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=eR(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function oR(e,t=""){let n=jr(e);if(da.test(n)||!nR.test(n))return null;let r=n.trimEnd().split(`
|
|
70
|
+
`).pop()||"";return rR.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 aR(e,t=""){let n=jr(e);if(da.test(n)||!jg.test(n))return null;let r=n.split(`
|
|
71
|
+
`),o=r.findIndex(i=>jg.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(sR);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 iR(e,t=""){let n=jr(e);return da.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 cR(e,t=""){let n=jr(e);return da.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 lR(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Sd.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 dR(e,t=""){let n=String(e);if(!uR.test(n)||Sd.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 fR(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Sd.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 Fg(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=oR(t,n)||aR(t,n)||iR(t,n)||cR(t,n)||tR(t,n);if(r)return r}if(e==="cursor"){let r=dR(t,n)||fR(t,n)||lR(t,n);if(r)return r}return ZI(t,{provider:e,box:n})}function Ug(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 XI,Dg,da,Lg,Bg,nR,rR,jg,sR,Sd,uR,kc=O(()=>{XI=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Dg=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;da=/(^|\n)\s*›/,Lg=/^\s*Welcome to Codex\b/;Bg=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;nR=/Successfully logged in\./,rR=/^\s*Press Enter to continue\.\s*$/;jg=/Enter this one-time code/,sR=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Sd=/Run Everything|Add a follow-up|Plan, search, build anything/;uR=/click this link to log in|Signing in with the browser/});function gR(e){let t=String(e||"");if(!Be.codex.block.test(t))return null;let n=jr(t);if(!Be.codex.block.test(n))return null;let r=n.match(wR);return{from:r?.[1]||null,to:r?.[2]||null}}function Yn(e){return new Promise(t=>setTimeout(t,e))}function Hg(e,t){let n=Date.now(),r=yR();for(let[s,a]of xc)n-a>=r&&xc.delete(s);let o=`${e}::${t}`;return xc.has(o)?!1:(xc.set(o,n),!0)}function xR(e){let t=0;for(let n of String(e).split(`
|
|
74
|
+
`))kR.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Td(e,t){let n=Be[e];if(!n?.busy)return{state:"idle",reason:null};if(Ed(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(t))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=xR(t);return e==="claude"&&r>0&&r<bR?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Ad(e,t){return Td(e,t).state}function Kg(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
75
|
+
`).slice(-vR).join(`
|
|
76
|
+
`);return SR.test(n)}function pa(e,t){return Ad(e,t)==="busy"}function Sc(e,t){return Ad(e,t)!=="idle"}function vc(e,t){let n=Be[e];return n?.up?n.up.test(t):!0}function TR(e,t){let n=Be[e];return n?Ed(e,t)?!1:n.up.test(t)&&Ad(e,t)==="idle":!0}function Ed(e,t){let n=Be[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 _d(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
77
|
+
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
78
|
+
`);return e}function Gg(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function Wg(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 qg(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null}={}){let s=Date.now()+n,a=0,i=null;for(await Nr(e).catch(()=>{});;){let c=_d(await Ee(e),o);if(TR(t,c)){await Yn(fa);return}if(Be[t]?.up&&!Be[t].up.test(c))if(await dn(e)){if(i??=Date.now(),Date.now()-i>=oa()){let l=Wg(t);throw Y({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,idleMs:Date.now()-i,error:l.message}).catch(()=>{}),l}}else i=null;else i=null;if(r&&t==="claude"&&hR.test(c)&&!Be.claude.up.test(c)){Y({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="cursor"&&mR.test(c)&&!Be.cursor.up.test(c)&&a<3){if(!Hg(e,"cursor-trust")){await Yn(vd);continue}a+=1,await Y({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:a}).catch(()=>{}),await Fe(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Yn(fa);continue}if(t==="codex"&&a<3){let l=gR(c);if(l){if(!Hg(e,`codex-update:${l.from}->${l.to}`)){await Yn(vd);continue}a+=1,await Y({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:a,update:`${l.from} -> ${l.to}`}).catch(()=>{}),await Fe(e,"3",{enter:!1,kind:"codex-update-skip"}),Pg({sessionName:e,from:l.from,to:l.to}),await Yn(fa);continue}}if(Date.now()>s){if(Ed(t,c)){let l=`${t} is blocked on an interactive prompt`;throw t==="codex"?l=Be.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"&&Be.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"&&Be.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"&&Be.claude.block.test(c)&&(l='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),Y({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,error:l}).catch(()=>{}),new Error(l)}if(await dn(e)){let l=Wg(t);throw Y({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,error:l.message}).catch(()=>{}),l}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),Y({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,paneBlocked:!!Be[t]?.block?.test(c),paneUp:!!Be[t]?.up?.test(c),paneBusy:!!Be[t]?.busy?.test(c)}).catch(()=>{}),await Yn(fa);return}await Yn(vd)}}function Vg({agent:e,agentCmd:t}={}){let n=t?Gn(t):Gn(e||"");return{label:n,realClaude:t?Mr(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 ma({cwd:e,sessionName:t,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Vg({agent:n,agentCmd:r});return!t||!o&&!s?null:Xn({cwd:e,sessionName:t,agent:n,agentCmd:r}).catch(()=>{})}async function Xn({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=pR(),bailOnDialog:a=!1,launchEcho:i=null}={}){let{label:c,realClaude:l,realCodex:u,realCursor:f}=Vg({agent:r,agentCmd:o});if(l&&e&&await Ln(e),u&&e&&await go(e),f&&e&&await yo(e),(l||u||f)&&n){await qg(n,c,s,{bailOnDialog:a,launchEcho:i});return}let d=Be[r]?r:Be[c]?c:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&d){await qg(n,d,s,{launchEcho:i});return}await Yn(fa)}var pR,vd,fa,Be,mR,hR,wR,yR,xc,bR,kR,SR,vR,Tc=O(()=>{tt();Us();Wi();qi();bc();kc();un();pR=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,vd=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,fa=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Be={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key|Successfully logged in\.[\s\S]*Press Enter to continue\./},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},mR=/Trust this workspace|\[a\]\s*Trust/,hR=/Resume from summary/i,wR=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;yR=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,xc=new Map;bR=68,kR=/^\s*[─━]{10,}\s*$/;SR=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,vR=15});function Gt(e){return e==="codex"?{enter:!0,submitDelayMs:AR}:e==="claude"?{enter:!0,submitDelayMs:ER}:{enter:!0}}var AR,ER,ha=O(()=>{AR=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),ER=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})()});function Lr(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of _R){let o=0;for(let s of IR[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function zg(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))RR.has(r)&&(t+=o||0),$R.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function wa(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 _R,IR,RR,$R,Id=O(()=>{_R=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],IR={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/]};RR=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),$R=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Zg(e){let t=wa(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||OR.has(r)||n.push(r);return n}function CR(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(Xg(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Xg(n,"assistant",o))}}return t}function Xg(e,t,n){let r=Lr(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 ga(e){let t=CR(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Zg(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function Ac(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(Zg(t))];if(!i.length)return[];let c=new Map;for(let u of i){let f=e.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(e.N-d+.5)/(d+.5));for(let[m,h]of f){let g=e.lengths[m],x=h+Jg*(1-Yg+Yg*(g/(e.avgdl||1))),k=p*(h*(Jg+1)/x);c.set(m,(c.get(m)||0)+k)}}let l=[];for(let[u,f]of c){let d=e.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var Jg,Yg,OR,Rd=O(()=>{Id();Jg=1.5,Yg=.75,OR=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 Ec from"node:fs/promises";import Qg from"node:path";async function MR(e){let t=Qg.join(e,".bostrat","events"),n;try{n=await Ec.readdir(t)}catch{return ga([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Ec.readFile(Qg.join(t,o),"utf8");for(let a of s.split(`
|
|
79
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return ga(r)}function NR(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}
|
|
80
|
+
${o}`.trim()||t.slice(0,2e3)}function PR(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Ac(e,t,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${ey}, 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(`
|
|
81
|
+
`):""}async function ty(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Ec.readFile(t,"utf8");if(a.includes(ey))return{appended:!1,reason:"already-enriched"};let i=await MR(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=PR(i,NR(a),{k:r,maxChars:o,exclude:s});return c?(await Ec.appendFile(t,`
|
|
78
82
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
79
|
-
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var
|
|
80
|
-
`)
|
|
81
|
-
`))
|
|
82
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Hg.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Hg.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(cR);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 Ce(e){return String(e||"").replace(/\s+/g," ").trim()}function Wg(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 qg(e,t,n,{busy:r=!1}={}){let o=da(e,n);return o&&xd(t,o)?"stuck":r||o?"submitted":"unknown"}function xd(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 Hg,cR,vd=C(()=>{Hg=/^\s*─{8,}\s*$/,cR=/^❯[ ]?(.*)$/});function mR(e){return lR.test(String(e||"").trimStart())}function Vg(e){let t=String(e||"");if(!mR(t.trimStart()))return null;let n=t.replace(uR,"").trim(),r=hR(n),o=[];for(let u of fR){let f=u.from.map(d=>Mo(r.get(d))).find(d=>d&&!Kg(d));f&&o.push({h:u.h,text:fa(f,dR)})}let s=Mo(r.get("Current Work"))||Mo(r.get("Primary Request and Intent"))||gR(n),a=bc(s),i=fa(yc(Ad(a[0]||"")),pR)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(fa(yc(Ad(l)),Td));for(let u of["Pending Tasks","Optional Next Step"]){let f=bc(Mo(r.get(u)))[0]||"";if(!(!f||Kg(f))){c.push(fa(`Left to do: ${yc(Ad(f))}`,Td));break}}if(!c.length){let u=bc(Mo(r.get("Problem Solving"))||"")[0];u&&c.push(fa(yc(u),Td))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function hR(e){let t=new Map,n=[];Sd.lastIndex=0;for(let r=Sd.exec(e);r;r=Sd.exec(e))n.push({name:wR(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 wR(e){return Gg.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Mo(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
83
|
-
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function gR(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Mo(t.split(/\n{2,}/).slice(0,2).join(" "))}function bc(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 Ad(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 yc(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Kg(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function fa(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=bc(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 lR,Gg,Sd,uR,dR,fR,pR,Td,zg=C(()=>{lR=/^This session is being continued from a previous conversation that ran out of context\./,Gg=["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"],Sd=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Gg.join("|")})[:*\t ]*$`,"gim"),uR=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,dR=420,fR=[{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"]}],pR=116,Td=168});function Yg(e){return Ed.has(String(e||"").replace(/^\//,"").toLowerCase())}function kc(e){let t=String(e||"").trim();if(!t)return null;let n=Vg(t);if(n)return n;let r=t.match(yR);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(bR);if(o)return{kind:"skill",name:o[1]};let s=t.match(kR);if(s){let i=s[1].toLowerCase();return Ed.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match($R);if(a&&Ed.has(a[1].toLowerCase()))return{kind:"suppress"};if(xR.test(t))return{kind:"suppress"};if(SR.test(t))return{kind:"suppress"};if(vR.test(t))return{kind:"suppress"};if(TR.test(t)){let i=l=>OR(t.match(l)?.[1].trim()||"")||null,c=i(RR);return{kind:"task",summary:i(AR)||"",taskId:i(ER),toolUseId:i(_R),status:i(IR)?.toLowerCase()||null,resultHead:c?c.length>Jg?`${c.slice(0,Jg).trimEnd()}\u2026`:c:null}}return null}function OR(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Xg(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 Zg(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var yR,bR,kR,xR,vR,SR,TR,AR,ER,_R,IR,RR,Jg,Ed,$R,_d=C(()=>{zg();yR=/^Base directory for this skill:\s*(\S+)/,bR=/^Run the "([^"]+)" workflow\./,kR=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,xR=/^<local-command-(?:stdout|caveat)>/i,vR=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,SR=/^Continue from where you left off\.$/,TR=/^<task-notification>/i,AR=/<summary>([\s\S]*?)<\/summary>/i,ER=/<task-id>([\s\S]*?)<\/task-id>/i,_R=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,IR=/<status>([\s\S]*?)<\/status>/i,RR=/<result>([\s\S]*?)(?:<\/result>|$)/i,Jg=600,Ed=new Set(["model","clear","compact","login","exit","quit"]);$R=/^\/([a-z0-9:_-]+)$/i});import CR from"node:crypto";function BR(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,ey)){let o=pa(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,MR)){let c=pa(i?.label);if(!c)return null;let l=pa(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<NR)return null;n.push({question:o,header:pa(r?.header).slice(0,PR),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Rd(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Qg.lastIndex=0;let t=null;for(let o;o=Qg.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=BR(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function $d(e,t,n){let r=CR.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Od(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=ey}={}){if(!Array.isArray(e)||!t||!Id.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||Rd(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=$d(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 ty(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=pa(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
83
|
+
- `).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 ey,ny=O(()=>{Rd();ey="Relevant prior discussion (verbatim, retrieved"});function ya(e,t){if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
84
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(ry.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(ry.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(DR);if(!a)return null;let i=[a[1].trim(),...s.slice(1).map(c=>c.trim())].filter(Boolean);return i.length?i.join(" "):null}function Ne(e){return String(e||"").replace(/\s+/g," ").trim()}function oy(e,t,{slackMs:n=2e4}={}){let r=Ne(t?.text);return!r||typeof t?.at!="number"?!1:(Array.isArray(e)?e:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=t.at-n&&Ne(o.text).includes(r))}function sy(e,t,n,{busy:r=!1}={}){let o=ya(e,n);return o&&$d(t,o)?"stuck":r||o?"submitted":"unknown"}function $d(e,t){let n=Ne(e),r=Ne(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var ry,DR,Od=O(()=>{ry=/^\s*─{8,}\s*$/,DR=/^❯[ ]?(.*)$/});function HR(e){return BR.test(String(e||"").trimStart())}function cy(e){let t=String(e||"");if(!HR(t.trimStart()))return null;let n=t.replace(jR,"").trim(),r=WR(n),o=[];for(let u of FR){let f=u.from.map(d=>Do(r.get(d))).find(d=>d&&!ay(d));f&&o.push({h:u.h,text:ba(f,LR)})}let s=Do(r.get("Current Work"))||Do(r.get("Primary Request and Intent"))||KR(n),a=Ic(s),i=ba(_c(Nd(a[0]||"")),UR)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(ba(_c(Nd(l)),Md));for(let u of["Pending Tasks","Optional Next Step"]){let f=Ic(Do(r.get(u)))[0]||"";if(!(!f||ay(f))){c.push(ba(`Left to do: ${_c(Nd(f))}`,Md));break}}if(!c.length){let u=Ic(Do(r.get("Problem Solving"))||"")[0];u&&c.push(ba(_c(u),Md))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function WR(e){let t=new Map,n=[];Cd.lastIndex=0;for(let r=Cd.exec(e);r;r=Cd.exec(e))n.push({name:qR(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 qR(e){return iy.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Do(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
85
|
+
`).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 KR(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Do(t.split(/\n{2,}/).slice(0,2).join(" "))}function Ic(e){let t=String(e||"").trim();if(!t)return[];let r=t.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function Nd(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 _c(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function ay(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function ba(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Ic(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>t)break;o=c}if(o.length>=Math.min(60,t*.4))return o;let s=n.slice(0,t),a=s.lastIndexOf(" ");return`${(a>t*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var BR,iy,Cd,jR,LR,FR,UR,Md,ly=O(()=>{BR=/^This session is being continued from a previous conversation that ran out of context\./,iy=["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"],Cd=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${iy.join("|")})[:*\t ]*$`,"gim"),jR=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,LR=420,FR=[{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"]}],UR=116,Md=168});function dy(e){return Pd.has(String(e||"").replace(/^\//,"").toLowerCase())}function Rc(e){let t=String(e||"").trim();if(!t)return null;let n=cy(t);if(n)return n;let r=t.match(GR);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(VR);if(o)return{kind:"skill",name:o[1]};let s=t.match(zR);if(s){let i=s[1].toLowerCase();return Pd.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(o$);if(a&&Pd.has(a[1].toLowerCase()))return{kind:"suppress"};if(JR.test(t))return{kind:"suppress"};if(XR.test(t))return{kind:"suppress"};if(YR.test(t))return{kind:"suppress"};if(ZR.test(t)){let i=l=>s$(t.match(l)?.[1].trim()||"")||null,c=i(r$);return{kind:"task",summary:i(QR)||"",taskId:i(e$),toolUseId:i(t$),status:i(n$)?.toLowerCase()||null,resultHead:c?c.length>uy?`${c.slice(0,uy).trimEnd()}\u2026`:c:null}}return null}function s$(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function fy(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 py(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var GR,VR,zR,JR,YR,XR,ZR,QR,e$,t$,n$,r$,uy,Pd,o$,Dd=O(()=>{ly();GR=/^Base directory for this skill:\s*(\S+)/,VR=/^Run the "([^"]+)" workflow\./,zR=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,JR=/^<local-command-(?:stdout|caveat)>/i,YR=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,XR=/^Continue from where you left off\.$/,ZR=/^<task-notification>/i,QR=/<summary>([\s\S]*?)<\/summary>/i,e$=/<task-id>([\s\S]*?)<\/task-id>/i,t$=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,n$=/<status>([\s\S]*?)<\/status>/i,r$=/<result>([\s\S]*?)(?:<\/result>|$)/i,uy=600,Pd=new Set(["model","clear","compact","login","exit","quit"]);o$=/^\/([a-z0-9:_-]+)$/i});import a$ from"node:crypto";function u$(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,hy)){let o=ka(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,i$)){let c=ka(i?.label);if(!c)return null;let l=ka(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<c$)return null;n.push({question:o,header:ka(r?.header).slice(0,l$),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function jd(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;my.lastIndex=0;let t=null;for(let o;o=my.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=u$(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Ld(e,t,n){let r=a$.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Fd(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=hy}={}){if(!Array.isArray(e)||!t||!Bd.has(n))return null;let s=null;for(let l=e.length-1;l>=0;l--){let u=e[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==t||s.agent!==n)return null;let a=s.ask?.questions||jd(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Ld(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 wy(e,t,n){let r=[];(e?.questions||[]).forEach((a,i)=>{let c=(t?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=ka(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
84
86
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
85
87
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
86
|
-
${o}`),s||""}var
|
|
88
|
+
${o}`),s||""}var Bd,hy,i$,c$,l$,my,ka,gy,Ud=O(()=>{Bd=new Set(["cursor","codex"]),hy=4,i$=4,c$=2,l$=24,my=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,ka=e=>typeof e=="string"?e.trim():"";gy=`
|
|
87
89
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
88
90
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
89
91
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -103,67 +105,67 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
103
105
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
104
106
|
anything you can decide yourself; keep asking in prose for questions without
|
|
105
107
|
enumerable options.
|
|
106
|
-
`});function DR(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 LR(e,t,n){let r=ry(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 Wn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,f=new Map;for(let g of e||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let h=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let k of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let _=k[0].split("/").pop().toLowerCase();f.has(_)||f.set(_,[]),f.get(_).push(g.payload?.ts??g.ts)}}let d=3*6e4,m=20*6e4,p=(g,h)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(_=>_>=h-d&&_<=h+m);for(let g of e||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let h=Ce(g.payload?.text);h&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(h))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let h=Ce(g.payload?.text);h&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(h))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let h=g.payload.note,k={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:ie(g)};h.toolUseId&&i.set(h.toolUseId,k);let _=String(h.summary||"").match(u);_&&l.set(_[1],k)}let w=new Map,y=new Map,b=null,v=new Map,R=new Map,A=new Map,V=new Map,O=new Map,M=new Map,T=new Map,q=new Map,G=new Map,ue=new Map,Q=new Map,N=new Map,$=new Map,F=()=>{for(let g of $.values())g.questions.length&&t.push(g);$.clear()},ce=null,Re=6e4,De=new Map,be=(g,h)=>{let k=o.get(g);if(!k?.length)return!1;let _=Ce(h),he=s.get(g);return k.some(ee=>{if(!_.startsWith(ee))return!1;let Te=Ce(_.slice(ee.length));return!!Te&&!!he?.has(Te)})};for(let g of e||[]){let h=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let k=h.note||kc(h.text);if(k){if(k.kind==="task"){if(k.toolUseId&&a.has(k.toolUseId))break;let _=String(k.summary||"").match(u);if(!k.toolUseId&&_&&c.has(_[1]))break}if(k.kind==="command"&&Yg(k.command))break;if(k.kind==="resume-summary"){t.push({id:g.id,kind:"resume-summary",title:k.title||"Context resumed from summary",bullets:k.bullets||[],sections:k.sections||[],rawChars:k.rawChars??null,ts:ie(g),box:g.boxId,agent:g.provider});break}k.kind!=="suppress"&&t.push({id:g.id,kind:"event",type:Zg(k),name:k.name??null,command:k.command??null,summary:k.summary??null,status:k.status??null,ts:ie(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${ie(g)}`)||g.source==="transcript"&&be(g.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:g.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:ie(g),box:g.boxId,agent:g.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(ce=null);break}for(let k of v.values())k.status="stale";v.clear();for(let k of A.values())k.status="stale";A.clear(),ce&&ce.box===g.boxId&&(t.push(DR(ce)),ce=null),t.push({id:g.id,kind:"message",role:"user",text:h.text,ts:ie(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...g.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let k=h.from||{};t.push({id:g.id,kind:"event",type:"peer-message",text:h.text,from:k.trackName||k.boxId||k.name||null,fromTrackId:k.trackId||null,ts:ie(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let k=pg(h,{codexFailedText:Or});if(k){t.push({id:g.id,kind:"event",type:"provider-error",cls:k.cls,status:k.status??void 0,text:k.text,provider:g.provider,legacy:!0,at:ie(g),ts:ie(g),box:g.boxId,agent:g.provider});break}F();let _=(h.images||[]).filter(je=>!p(je,ie(g)));if(!String(h.text||"").trim()&&!_.length)break;if(g.provider==="cursor"){let je=`${g.boxId}|${h.text}`;if(n.has(je))break;n.add(je)}(!ce||ce.box===g.boxId)&&(ce=null);let he=g.provider==="codex"?(h.tools||[]).filter(je=>!ad.has(je)&&!na.has(je)):h.tools||[],ee=h.text,Te=null;if(Id.has(g.provider)){let je=Rd(String(h.text||""));je&&(ee=je.text,Te={questions:je.questions})}t.push({id:g.id,kind:"message",role:"assistant",text:ee,ts:ie(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...Te?{ask:Te}:{},...he.length?{tools:he}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},..._.length?{images:_}:{}}),Te&&(ce={id:g.id,groupId:$d(g.provider,h.sourceId||g.id,Te.questions),provider:g.provider,box:g.boxId,questions:Te.questions,ts:ie(g)});break}case"job.started":{if(!h.id)break;let k=ie(g),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let Le=l.get(h.name);Le&&Le.ts>=k&&(_=Le)}let he={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:_?_.status==="killed"?"stopped":_.status&&_.status!=="completed"?"failed":"done":"running",startedTs:k,finishedTs:_?.ts??null,summary:_?.summary??null,resultHead:_?.resultHead??null},ee=De.get(g.boxId),Te=ee?.jobs[ee.jobs.length-1]?.startedTs;if(ee&&Te!=null&&k-Te<=Re)ee.jobs.push(he);else{let Le={id:g.id,kind:"job-dock",dockId:h.id,ts:k,box:g.boxId,agent:g.provider,jobs:[he]};De.set(g.boxId,Le),t.push(Le)}break}case"handoff.done":t.push(wt(g,"lead-handoff"));break;case"subtask.delegated":t.push({...wt(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":t.push(wt(g,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let _ of v.values())_.status="stale";v.clear();for(let _ of A.values())_.status="stale";A.clear()}let k=wt(g,`pr-${h.change}`);if(k.box=g.boxId??null,jR.has(h.change)&&h.number!=null){let _=LR(w,y,h);if(w.size>=2){for(let ee of y.values())for(let Te of ee)Te.grouped=!0;k.groupPrs=[...w.values()];let he=[k];for(let ee of w.keys())y.set(ee,he)}else{let he=y.get(_)??[];he.push(k),y.set(_,he)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=ry(h),he=b&&!b.complete||w.size>=2;if(w.has(_))w.delete(_),y.delete(_);else if(h.number!=null)for(let ee of[...w.keys()])ee.endsWith(`#${h.number}`)&&(w.delete(ee),y.delete(ee));if(he&&h.number!=null){(!b||b.complete)&&(b={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:ie(g),ts:ie(g),box:g.boxId,agent:g.provider},t.push(b)),b.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:ie(g)}),b.remaining=[...w.values()],b.complete=w.size===0;break}}t.push(k);break}case"pr.offered":{if(!h.offerId)break;for(let _ of v.values())_.status="stale";v.clear();for(let _ of A.values())_.status="stale";A.clear();let k={id:g.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:ie(g),box:g.boxId,agent:g.provider};v.set(h.offerId,k),R.set(h.offerId,k),t.push(k);break}case"pr.offer.answered":{let k=R.get(h.offerId);k&&(k.status=h.action||"dismiss",k.actions=Array.isArray(h.actions)?h.actions:null,k.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(k.existingPr={number:h.existingPr.number??null}),v.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let _ of A.values())_.status="stale";A.clear();let k={id:g.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:ie(g),box:g.boxId,agent:g.provider};A.set(h.offerId,k),V.set(h.offerId,k),t.push(k);break}case"pr.exists.answered":{let k=V.get(h.offerId);k&&(k.status=h.choice||"dismiss",A.delete(h.offerId));break}case"box.state.changed":{let k=h.change==="model"?"model-change":h.change;if(!k)break;let _=wt(g,k);if(k==="model-change"&&(_.to=h.model),k==="node-update"&&_.latest){let he=t.findIndex(ee=>ee.kind==="event"&&ee.type==="node-update"&&ee.latest===_.latest);if(he>=0){let ee=t[he];t[he]={..._,ts:ee.ts,at:ee.at,firstAt:ee.firstAt??ee.ts,updatedAt:_.ts};break}}t.push(_);break}case"track.transferred":case"track.recovered":t.push(wt(g,"track-moved"));break;case"view.state.changed":{if(h.change!=="created"||h.restarted)break;t.push({id:g.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:ie(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{t.push({id:g.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:ie(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:g.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:ie(g),box:g.boxId,agent:g.provider});break}if(h.change!=="created")break;t.push({id:g.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:ie(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(h.change!=="created")break;t.push({id:g.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:ie(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;t.push({id:g.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:ie(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{t.push({id:g.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:ie(g),box:g.boxId,agent:g.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let _ of T.values())_.box===(h.box||g.boxId)&&(_.status="stale",T.delete(_.offerId));let k={id:g.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:ie(g),box:h.box||g.boxId,agent:h.agent||g.provider};T.set(h.offerId,k),q.set(h.offerId,k),t.push(k);break}case"box.fullscreen.answered":{let k=q.get(h.offerId);k&&(k.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",T.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let _ of O.values())_.key===h.key&&(_.status="stale",O.delete(_.offerId));let k={id:g.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:ie(g),box:g.boxId,agent:g.provider};O.set(h.offerId,k),M.set(h.offerId,k),t.push(k);break}case"ticket.move.answered":{let k=M.get(h.offerId);k&&(k.status=h.action==="moved"?"moved":"dismiss",O.delete(h.offerId));break}case"ticket.state.changed":{for(let k of O.values())k.key===h.key&&k.to===h.to&&(k.status="stale",O.delete(k.offerId));if(h.mode==="offer")break;t.push(wt(g,"ticket-state"));break}case"ticket.commented":{t.push({id:g.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:ie(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":t.push(wt(g,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let _ of G.values())_.status="stale";G.clear();let k={id:g.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:ie(g),box:g.boxId,agent:g.provider};G.set(h.proposalId,k),ue.set(h.proposalId,k),t.push(k);break}case"ticket.create.answered":{let k=ue.get(h.proposalId);k&&(k.status=h.action==="created"?"created":"dismiss",k.createdCount=h.createdCount??null,k.failedCount=h.failedCount??null,G.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let _ of Q.values())_.status="stale";Q.clear();let k={id:g.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,status:"open",ts:ie(g),box:g.boxId,agent:g.provider};Q.set(h.proposalId,k),N.set(h.proposalId,k),t.push(k);break}case"loop.create.answered":{let k=N.get(h.proposalId);k&&(k.status=h.action==="created"?"created":"dismiss",Q.delete(h.proposalId));break}case"loop.created":{let k=h.proposalId?N.get(h.proposalId):null;k&&(k.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"ticket.created":{let k={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},_=h.proposalId?ue.get(h.proposalId):null;if(_){_.created=k;break}t.push({id:g.id,kind:"ticket-created",...k,ts:ie(g),box:g.boxId,agent:g.provider});break}case"error":{if(!h.kind)break;t.push(wt(g,h.kind));break}case"provider.error":t.push({...wt(g,"provider-error"),provider:g.provider,box:g.boxId,agent:g.provider});break;case"usage.cap.hit":t.push(wt(g,"usage-cap-hit"));break;case"usage.cap.warning":t.push(wt(g,"usage-cap-warning"));break;case"provider.connected":{t.push({id:g.id,kind:"event",type:"provider-connected",provider:h.provider||g.provider,ts:ie(g),at:ie(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(ce=null,h.control||!h.question)break;let k=h.groupId||h.promptId;if(!k)break;let _=$.get(k);_||(_={id:g.id,kind:"prompt",groupId:k,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:h.questionTotal??1,questions:[],ts:ie(g)},$.set(k,_));let he=h.questionIndex??0,ee={index:he,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},Te=_.questions.findIndex(Le=>Le.index===he&&Ce(Le.question)===Ce(ee.question));Te>=0?_.questions[Te]=ee:_.questions.push(ee),_.questions.sort((Le,S)=>Le.index-S.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(t.push(_),$.delete(k));break}default:break}}return UR(t.sort((g,h)=>(g.ts??0)-(h.ts??0)))}function UR(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<=FR){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 Md(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:HR(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??Wn(e)).filter(o=>o.kind==="message");return sg(n,r)}function HR(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Ro(e.command)?t:Fn(e.command,e.description||"")}function wt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:ie(e),ts:ie(e)}}function oy(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)+WR,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&qR.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function sy(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!GR.has(r.type)||!r.box)continue;let o=(r.ts??0)+KR,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 ay(e){return(e||[]).filter(t=>VR.has(t?.kind)&&t.status==="open")}var ie,jR,ry,FR,WR,qR,KR,GR,VR,Nd=C(()=>{$o();Cr();ic();vd();_d();Cd();ie=e=>e.payload?.ts??e.ts;jR=new Set(["opened","ci-fixed","reconciled","pushed"]),ry=e=>`${e.repo??""}#${e.number}`;FR=2*6e4;WR=12e4,qR=new Set(["archived","unarchived","opened"]);KR=2500,GR=new Set(["pr-opened","pr-merged","pr-auto-armed"]);VR=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","fullscreen-offer"])});function ZR(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 e$(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 t$(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<YR&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<XR&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function uy(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=ZR(e),s=e$(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Wn(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-zR);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(ly[r]??ly.failover),l.push(""),o.lastPush?.sha){let d=cy(n,o.lastPush.ts),m=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${m}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=cy(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=t$(e??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${QR(d.text,JR)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
|
|
107
|
-
`);return f.length<=
|
|
108
|
-
|
|
109
|
-
[\u2026 brief truncated]`}var
|
|
110
|
-
|
|
111
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function d$(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(my.test(s)){if(r.length>=Ty)continue;let a=u$(s.replace(my,"").replace(/[.;,:]+$/,""),Ay,c$);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>py&&(o=`${o.slice(0,py-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Ey(e,t,{timeoutMs:n=9e4}={}){let r=Kn.join(t,a$),o=Kn.join(r,i$);await qn.mkdir(r,{recursive:!0});let s=await xc(o),a=await ae(e);if(!a)throw new Error(`session "${e}" not found`);return await vc(e,a,l$(o)),await My(o,s,n),d$(await qn.readFile(o,"utf8"))}function _y(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===ky||n===Sy;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(by)||n===r$||r.startsWith(xy)||r.startsWith(vy)}function Iy(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-f$;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 Ry(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 $y(e,{windows:t=[]}={}){if(_y(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 Sc(e){return Pd.get(e)||null}async function Oy(e){let t=Pd.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:Kn.join(n.cwd,wy,gy)};return Pd.set(e,r),h$(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function h$(e,t){e.step="summarize",await Cy(e.name,t),e.step="clear";let n=await ae(e.name);await vc(e.name,n,"/clear"),await jd(p$),e.step="resume",await vc(e.name,n,s$),e.step="done",e.finishedAt=Date.now()}async function Cy(e,t,{timeoutMs:n=m$}={}){let r=Kn.join(t,wy),o=Kn.join(r,gy);await qn.mkdir(r,{recursive:!0});let s=await xc(o),a=await ae(e);await Y({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await vc(e,a,o$(o)),await My(o,s,n),await Y({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await xc(o)});let i=await jg(t,o,{exclude:u=>_y(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 I({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await qn.readFile(o,"utf8");await I({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...yy(u),ts:Date.now()}})}catch{}}await Pu(t);let l=await w$(r,o);return c&&await Mw(c,l||o,{box:e,agent:a?.agent??null}),o}async function Bd(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 Cy(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Dd(e,t,{trackName:n="this Track"}={}){let r=await B({id:e,cwd:t}),o=uy(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Kn.join(t,hy);return await qn.mkdir(Kn.dirname(s),{recursive:!0}),await qn.writeFile(s,o,"utf8"),await I({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...yy(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),hy}async function xc(e){try{return(await qn.stat(e)).mtimeMs}catch{return 0}}async function My(e,t,n){let r=Date.now()+n;for(;;){if(await xc(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await jd(2e3)}}async function w$(e,t){try{if(!n$.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Kn.join(e,`${n}.md`);return await qn.copyFile(t,r),r}catch{return null}}function jd(e){return new Promise(t=>setTimeout(t,e))}async function vc(e,t,n){if(t){let o=t.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":t.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":t.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await Hn({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 jd(150),await Ao(e,oc,{kind:"drive-wipe"}),await Fe(e,n,{kind:"orchestration-drive",...jt(r.agent)})}var wy,gy,fy,by,ky,xy,r$,o$,s$,vy,Sy,a$,i$,py,Ty,Ay,c$,l$,my,u$,f$,p$,m$,Pd,hy,Ld=C(()=>{Xe();mc();nn();ca();Lg();Ks();le();Yi();dy();wy=".bostrat/handoff",gy="latest.md",fy=32*1024;by="Checkpoint: write a work-in-progress handoff",ky="HANDOFF WRITTEN",xy="Resume from .bostrat/handoff/latest.md",r$="/clear",o$=e=>`${by} 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 ${ky} and take no further action.`,s$=`${xy}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,vy="Beat: write a",Sy="BEAT WRITTEN",a$=".bostrat/arc",i$="beat.txt",py=200,Ty=4,Ay=5,c$=36,l$=e=>`${vy} 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 ${Ty} further lines, each starting with "- " and naming ONE piece in at most ${Ay} words \u2014 a label, not a sentence (e.g. "- stale working flag on poll"). Write nothing else in the file. When the file is written, reply only with ${Sy} and take no further action.`,my=/^(?:[-–—*•·]|\d+[.)])\s+/,u$=(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};f$=6e5;p$=3500,m$=3e5,Pd=new Map;hy=".bostrat/handoff/status-latest.md"});function Py(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!g$.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 y$(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:Py(o.from,"from",n),toMs:Py(o.to,"to",n)}))}return t}function k$(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 x$(e){if(!e)return null;let t=String(e).trim();return Fd[t]?t:Ny[t]?Ny[t]:Object.keys(Fd).find(r=>t.startsWith(r))||null}function By(e,t){let n=x$(e);if(!n)return null;let r=k$(t);return r===null?null:b$[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function Gn(e,t={},n){let r=By(e,n);return r?((t.input||0)*r.input+(t.output||0)*r.output+(t.cacheWrite||0)*r.cacheWrite+(t.cacheRead||0)*r.cacheRead)/1e6:0}var Fd,Ny,g$,b$,Dy,ma=C(()=>{Fd={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Ny={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"},g$=/^\d{4}-\d{2}-\d{2}$/;b$=y$(Fd);Dy=(e,t)=>By(e,t)!==null});function v$(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Ly(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function sn(e){let t=e.payload?.model||"unknown",n=v$(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.source==="vendor",s=r,a=!1;if(n&&!o){let l=Gn(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&&!Dy(t,e.ts);return{usd:s,model:t,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function Vn(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=jy();for(let d of n){let{usd:m,model:p,tokens:w,repriced:y,unpriced:b,asRecorded:v}=sn(d);i+=v;let R=d.provider||"unknown",A=d.boxId||"unattributed";if(y&&c++,b&&(l++,u.add(p)),a+=m,Ly(f,w),r[p]||={usd:0,tokens:jy(),events:0},r[p].usd+=m,r[p].events+=1,Ly(r[p].tokens,w),o[R]||={usd:0,events:0},o[R].usd+=m,o[R].events+=1,s[A]||={usd:0,events:0,provider:R,models:new Set,label:t[A]||A},s[A].usd+=m,s[A].events+=1,p!=="unknown"&&s[A].models.add(p),d.epoch!=null){let V=(s[A].epochs||={})[d.epoch]||={usd:0,events:0,provider:R,models:new Set};V.usd+=m,V.events+=1,V.provider=R,p!=="unknown"&&V.models.add(p)}}return{totalUsd:No(a),asRecordedUsd:No(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Tc(r,d=>({...d,usd:No(d.usd)})),byProvider:Tc(o,d=>({...d,usd:No(d.usd)})),byBox:Tc(s,d=>({usd:No(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Tc(d.epochs,m=>({usd:No(m.usd),events:m.events,provider:m.provider,models:[...m.models]}))}:{}}))}}var jy,No,Tc,Pr=C(()=>{ma();jy=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});No=e=>Math.round(e*1e4)/1e4,Tc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function S$(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 Fy(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,m]=f.split("#");return`${d.split("/").pop()}#${m}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Ud(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Ud(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${Ud(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Vn(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?S$(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Ud,Uy=C(()=>{Pr();Ud=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as T$}from"node:child_process";import{promisify as A$}from"node:util";import{randomUUID as E$}from"node:crypto";async function Hd(e,t){let n=Date.now()+t;for(;;){if(await To(e))return!0;if(Date.now()>n)return!1;await Ac(I$)}}async function $$(e,t){let n=Wy[t]||Wy.default;n.escape&&(await Ie(e,["Escape"],{kind:"swap-exit"}),await Ac(150)),n.wipe&&await Ao(e,oc,{kind:"swap-exit"}),await sc(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=jt(t).submitDelayMs||0;await Ac(Math.max(r,250)),await Ie(e,["Enter"],{kind:"swap-exit"})}async function O$(e,t){try{let{stdout:n}=await Po(j,W("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function qy(e){await Po(j,W("respawn-pane","-k","-t",e))}async function Wd(e,t={}){if(!Ae(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||Dn(n),a=Number(t.exitTimeoutMs)||_$;await Y({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await To(e)&&(await $$(e,o),!await Hd(e,a)&&(i=!0,await Y({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await qy(e),!await Hd(e,Hy))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Ir(n),l=c?Ci():null,u=await O$(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Po(j,W("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Po(j,W("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?Zi(y,l):y}if(f&&!i&&(await qy(e),!await Hd(e,Hy)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,m=t.resumeSessionId||E$(),p=d?n:Gu(n,m,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(p);return c&&await On(r.cwd),s==="codex"&&await po(r.cwd),s==="cursor"&&await mo(r.cwd),await Rr(e),await Po(j,W("set-option","-t",e,"@bostrat-sid",m)),await Po(j,W("set-option","-t",e,"@bostrat-agent",s)),await Ac(R$),await Fe(e,p,{kind:"swap-launch"}),await Y({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:m,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:m,agent:s,pinned:w,forced:i,envRespawned:f}}var Po,Ac,_$,Hy,I$,R$,Wy,Ky=C(()=>{Xe();js();Ni();Pi();ca();nn();Po=A$(T$),Ac=e=>new Promise(t=>setTimeout(t,e)),_$=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Hy=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,I$=250,R$=400,Wy={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 Gy(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await rt(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
112
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let
|
|
113
|
-
`).trim():"",
|
|
114
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let f=r[u].match(
|
|
115
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
116
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
117
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
118
|
-
`),o=
|
|
119
|
-
`),o=
|
|
120
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
121
|
-
`)].reverse().find(a=>
|
|
122
|
-
`),o=
|
|
123
|
-
`),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match(
|
|
124
|
-
`),o=
|
|
125
|
-
`).pop()||"")?!0:
|
|
126
|
-
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function
|
|
127
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
128
|
-
`).filter(Boolean);return t&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function
|
|
129
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
130
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
131
|
-
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
132
|
-
`),n=t.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
133
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function
|
|
134
|
-
`),n=t.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(t[n]),o=[];for(let s=n+1;s<t.length;s++){let a=t[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function Yb(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&&!Vb.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=zb.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!$O.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ka(e,t,{timeoutMs:n=IO}={}){let r=Date.now()+n;for(;;){let o=await Se(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Pc(RO)}}async function MO(e,t){return await Fe(e,`/model ${t.model}`,jt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function NO(e,t){await rc(e),await Fe(e,"/model",{enter:!1}),await Pc(250),await Ie(e,["Enter"],{kind:"model-switch"});let n=await ka(e,Wb);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=CO(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Fe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await ka(e,i=>qb(i)||!Wb(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?Vb.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 Fe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ka(e,i=>!qb(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 PO(){let e=Hb.join(process.env.CURSOR_CONFIG_DIR||Hb.join(_O.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await EO.readFile(e,"utf8"))?.model||null}catch{return null}}async function BO(e,t){await rc(e),await sc(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await ka(e,Kb),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 ka(e,Kb,{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 Pc(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 Pc(800);let a=await PO();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 Xb(e,t,n){return e==="codex"?NO(t,n):e==="cursor"?BO(t,n):MO(t,n)}var Pc,IO,RO,Gb,$O,OO,Vb,zb,Zb=C(()=>{Xe();ca();Pc=e=>new Promise(t=>setTimeout(t,e)),IO=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,RO=300,Gb=[{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"}],$O=new Set([...Gb.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"]),OO=[{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"}],Vb=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),zb=[{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 Dc(e){let t=e||{},n=DO(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function DO(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:st(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:st(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:st(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:st(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:st(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:st(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:st(e.agent),payload:{...Yn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:st(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Yn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:st(e.agent),payload:{change:"subtask-resumed",...Yn(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",...Yn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Yn(e)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Yn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:st(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:st(e.agent),payload:{...Yn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Yn(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:st(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function tk(e,t){let n=Dc(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;vf(()=>I(r,n))}async function nk(e){if(!(!e?.id||Qb.has(e.id))){Qb.add(e.id);try{let t=await B(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=Dc(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(e,o)}}catch{}}}function Yn(e){let{type:t,at:n,...r}=e;return r}function rk(e,t,n){!n?.promptId||xf.get(e.id)===n.promptId||(xf.set(e.id,n.promptId),vf(()=>I(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:st(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 Sf(e,t,n,r={}){xf.delete(e.id),vf(()=>I(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:st(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var st,Bc,vf,ek,Qb,xf,Tf=C(()=>{le();st=e=>e||null;Bc=Promise.resolve(),vf=e=>(Bc=Bc.then(e).catch(()=>{}),Bc),ek=()=>Bc;Qb=new Set;xf=new Map});function HO(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function lk(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of HO(n).reverse()){if(!jO.test(r))continue;let o=r.endsWith("?")&&r.length<=UO&&FO.test(r);if(LO.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function dk(e){let t=String(e||""),n=[];WO.test(t)&&n.push("commit"),uk.test(t)&&n.push("rebase"),qO.test(t)&&n.push("push");let r=GO.test(t);!r&&KO.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 fk(e){return uk.test(String(e||""))}function Vo(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(m=>`${m.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let m=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${m} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${m} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let m=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",p=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${p}${m}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${m}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${p}${m}`:`push this branch${l} to the remote${m}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,m=>m.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}function pk(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 mk(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<=VO&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function hk(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+zO||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function wk(e){let t=(Array.isArray(e)?e:[]).filter(r=>ok[r]).map(r=>ok[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 sk,ak,ik,ck,jO,LO,FO,UO,Go,WO,uk,qO,KO,GO,VO,zO,ok,zo=C(()=>{sk=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,ak=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,ik=/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,ck=/(?<!\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,jO=new RegExp(`\\b(?:${sk}|${ak}|${ik}|${ck})\\b`,"i"),LO=/\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,FO=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,UO=90;Go=["commit","rebase","push","pr"],WO=new RegExp(`\\b(?:${sk})\\b`,"i"),uk=new RegExp(`\\b(?:${ck})\\b`,"i"),qO=new RegExp(`\\b(?:${ak})\\b`,"i"),KO=new RegExp(`\\b(?:${ik})\\b`,"i"),GO=/\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;VO=3e4;zO=2500;ok={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import JO from"node:crypto";import jc from"node:fs/promises";import gk from"node:path";import YO from"node:os";async function $e(){return Xn||(Af??=jc.readFile(_f,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Xn=e,!t&&ve()){let{records:n,source:r}=await Pe("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;Xn[X(i.repo,i.number)]=i}Object.keys(Xn).length&&await If().catch(()=>{})}}return Xn}).finally(()=>Af=null),Af)}async function If(){let e=JSON.stringify({links:Xn},null,2),t=`${_f}.${process.pid}.tmp`;await jc.mkdir(yk,{recursive:!0}),await jc.writeFile(t,e),await jc.rename(t,_f)}async function He(e){Ef=Ef.then(If,If),await Ef.catch(()=>{});let t=Xn?.[e];t&&Ye("prlinks",{id:bk(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function ut({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 $e(),c=X(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:XO.has(a),firstSeen:Date.now()},await He(X(e,t)),{link:i[c],created:!0})}async function cn(e,t){if(!e||t==null)return null;let n=(await $e())[X(e,t)];return!n||n.announced?null:(n.announced=!0,await He(X(e,t)),n)}async function Ze(e,t){return!e||t==null?null:(await $e())[X(e,t)]??null}async function Zn(e,t,n){if(!e||t==null)return null;let r=(await $e())[X(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await He(X(e,t)),r):null}async function kk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await $e())[X(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await He(X(e,t)),o):null}async function xk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await $e())[X(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await He(X(e,t)),o):null}async function Lc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await $e())[X(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await He(X(e,t)),r)}async function Fc(e,t){if(!e||t==null)return null;let n=(await $e())[X(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await He(X(e,t)),n)}async function Uc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await $e())[X(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await He(X(e,t)),r)}async function Hc(e,t){if(!e||t==null)return null;let n=(await $e())[X(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await He(X(e,t)),n)}async function vk(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await $e())[X(e,t)];return r?(r.baseFoldAt=n,await He(X(e,t)),r):null}async function Sk(e,t){if(!e||t==null)return null;let n=(await $e())[X(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await He(X(e,t)),n)}async function Tk(e,t,n){if(!e||t==null||!n)return null;let r=(await $e())[X(e,t)];return r?(r.lastHeadSha=n,await He(X(e,t)),r):null}async function xa(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await $e())[X(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await He(X(e,t)),r):null}async function qr(e,t,n=!0){if(!e||t==null)return null;let r=(await $e())[X(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await He(X(e,t)),r):null}async function va(e,t,n=!0){if(!e||t==null)return null;let r=(await $e())[X(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await He(X(e,t)),r):null}async function Ak(e,t,n){if(!e||t==null)return null;let r=(await $e())[X(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await He(X(e,t)),r}async function Sa(e,t){if(!e||t==null)return null;let n=(await $e())[X(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await He(X(e,t)),n)}async function Ek(){return Object.values(await $e()).filter(e=>!ZO.has(e.state))}async function Kr(){return Object.values(await $e()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function _k(){if(!ve())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Pe("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await $e(),r=0;for(let[o,s]of Object.entries(n))await Ye("prlinks",{id:bk(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var yk,_f,X,bk,Xn,Af,Ef,XO,ZO,Qn=C(()=>{Bn();yk=process.env.BOSTRAT_STATE_DIR||gk.join(process.env.HOME||YO.tmpdir(),".bostrat"),_f=gk.join(yk,"pr-links.json"),X=(e,t)=>`${e}#${Number(t)}`,bk=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=JO.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Xn=null,Af=null,Ef=Promise.resolve();XO=new Set(["api","observed","reconciled"]);ZO=new Set(["merged","closed","lost"])});import QO from"node:fs";import Rf from"node:fs/promises";import Ok from"node:path";import eC from"node:os";function Nk(e,t=!1){if(!t&&e-Rk<tC)return;Rk=e;let n=JSON.stringify({windowStart:Jo,byCaller:Object.fromEntries(ln),lastHour:er});$k=$k.then(()=>Rf.mkdir(Ck,{recursive:!0})).then(()=>Rf.writeFile(`${Wc}.tmp`,n)).then(()=>Rf.rename(`${Wc}.tmp`,Wc)).catch(()=>{})}function Pk(e){e-Jo<Mk||(er={start:Jo,total:qc(ln),byCaller:Object.fromEntries(ln)},er.total&&console.log(`[github] ${er.total} calls last hour`,er.byCaller),Jo=e,ln=new Map,Nk(e,!0))}function un(e="other",t=Date.now()){Pk(t),ln.set(e,(ln.get(e)||0)+1),Nk(t)}function Bk(e=Date.now()){return Pk(e),{windowStart:Jo,total:qc(ln),byCaller:Object.fromEntries(ln),lastHour:er,backoffUntil:Gr>e?Gr:null}}function tr(e){return nC.test(String(e?.message||e||""))}function nr(e,t=Date.now()){return Gr<=t&&console.warn(`[github] rate limited \u2014 backing off ${Ik/6e4} min:`,String(e?.message||e||"").slice(0,200)),Gr=Math.max(Gr,t+Ik),Gr}function Kc(e=Date.now()){return Math.max(0,Gr-e)}var Ck,Wc,Mk,Ik,tC,Jo,ln,er,Gr,Rk,qc,$k,nC,Ta=C(()=>{Ck=process.env.BOSTRAT_STATE_DIR||Ok.join(process.env.HOME||eC.tmpdir(),".bostrat"),Wc=Ok.join(Ck,"github-metrics.json"),Mk=60*6e4,Ik=5*6e4,tC=6e4,Jo=Date.now(),ln=new Map,er=null,Gr=0,Rk=0,qc=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(QO.readFileSync(Wc,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Mk?(Jo=e.windowStart,ln=new Map(Object.entries(e.byCaller||{})),er=e.lastHour||null):qc(new Map(Object.entries(e.byCaller||{})))&&(er={start:e.windowStart,total:qc(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}$k=Promise.resolve();nC=/rate limit|too quickly|abuse detection|HTTP 429/i});import Dk from"node:fs/promises";import jk from"node:path";import rC from"node:os";function Vc({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};Gc.push(l),Gc.length>sC&&Gc.shift();let u=$f.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)),$f.set(e,u),aC(l),l}async function aC(e){try{await Dk.mkdir(Lk,{recursive:!0}),await Dk.appendFile(oC,JSON.stringify(e)+`
|
|
135
|
-
`)}catch{}}function Fk(){let e={};for(let[t,n]of $f)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:Gc.slice(-20)}}var Lk,oC,sC,Gc,$f,zc=C(()=>{Lk=process.env.BOSTRAT_STATE_DIR||jk.join(process.env.HOME||rC.tmpdir(),".bostrat"),oC=jk.join(Lk,"github-latency.ndjson"),sC=100,Gc=[],$f=new Map});import{execFile as iC}from"node:child_process";import{promisify as cC}from"node:util";async function lC(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Xo("git",["-C",e,"rev-parse","HEAD"]),Xo("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await I({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function gt(e,t,n={}){let{caller:r="prs",...o}=n,s=await Pt({cwd:e});un(r);try{let{stdout:a}=await Xo("gh",t,{cwd:e,...uC,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw tr(a)&&nr(a),a}}async function Mf(){if(Jc&&Date.now()-Jc.at<6e4)return Jc.data;let e;try{un("auth");let t=await Xo("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 Us();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Jc={at:Date.now(),data:e},e}async function dC(e){try{let{stdout:t}=await Xo("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Hk(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 Of(e){let t=(e||[]).map(Hk),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 Pf(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function qk(e,t){if(!t)return null;let n=await de(e);if(!n)return null;try{let r=await gt(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 Kk(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 Gk(e,t){let n=await qk(e,t);return n===null?{...Of([]),unavailable:!0}:Of(Kk(n))}function Yc(e,t=Of(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 pC(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Yo.delete(s);continue}r.add(s),Yo.has(s)||Yo.set(s,n),o.mergeabilityPending=n-Yo.get(s)<fC}for(let o of Yo.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Yo.delete(o);return t}async function Zo(e,t){let n=await dn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await gt(n.path,["pr","view",String(r),"--json",Nf]);return Yc(JSON.parse(o))}catch(o){if(!Pf(o))throw o;let s=await gt(n.path,["pr","view",String(r),"--json",Wk]),a=JSON.parse(s);return Yc(a,await Gk(n.path,a.headRefOid))}}async function Lt(e,{caller:t="prs"}={}){try{let n=await gt(e,["pr","list","--json",Nf,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Yc(r)).sort(Uk)}catch(n){if(!Pf(n))throw n;let r=await gt(e,["pr","list","--json",Wk,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Yc(o,await Gk(e,o.headRefOid))))).sort(Uk)}}function Ut(){Aa.clear()}async function wC(e,t){if(!e)return t;for(let n of t){let r=await Ze(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 Lc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Uc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function Vk(){let e=await Mf();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await en())await dC(a.path)&&t.push(a);let n=Date.now(),r=Kc(n)>0,o=await Promise.all(t.map(async a=>{let i=Aa.get(a.path),c=i?.pending?hC:mC;if(i&&(r||n-i.at<c))return i.entry;try{let l=await de(a.path).catch(()=>null),u=pC(a.path,await Lt(a.path,{caller:"tab"}),n);if(await wC(l,u),i?.entry?.prs)for(let m of u)i.entry.prs.find(w=>w.number===m.number)?.checks?.pending>0&&m.checks.total>0&&m.checks.pending===0&&Vc({change:m.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:m.number,githubAt:m.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(m=>m.mergeabilityPending||m.checks.pending>0||m.autoMergeArmed);return Aa.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Ft(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Aa.keys())s.has(a)||Aa.delete(a);return{auth:e,repos:o}}async function dn(e){let t=(await en()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Vr(e){return ho(e)}async function Qo(e,t,{method:n="squash"}={}){let r=await dn(e);if(!Ea.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 Pt({cwd:r.path})).GH_TOKEN){let a=await _u().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await gt(r.path,s)}catch(a){throw new Error(Ft(a))}finally{Ut()}return{ok:!0,number:o}}async function _a(e,t){let n=await dn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await gt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Ft(s))}finally{Ut()}let o=await de(n.path).catch(()=>null);return o&&await vk(o,r).catch(()=>{}),{ok:!0,number:r}}async function Bf(e,t){let n=await dn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await gt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Ft(o))}finally{Ut()}return{ok:!0,number:r}}function Df(e){return e.green&&!e.isDraft}function kC(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 zk(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Df(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Jk(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Yk(){let e={};for(let[t,n]of Cf)e[t]={...n};return e}async function Xk(e,{method:t="merge",onMerged:n=null}={}){let r=await dn(e);if(!Ea.has(t))throw new Error(`invalid merge method: ${t}`);if(Cf.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Lt(r.path)).filter(Df).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 Cf.set(r.path,s),vC(s,o,{listPrs:()=>Lt(r.path),merge:a=>Qo(r.path,a,{method:t}),sleep:bC,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function xC(e,t){let n=Date.now()+(t.settleMaxMs??yC),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(gC)}}async function vC(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 xC(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: ${Ft(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(!Df(a)){e.skipped.push({number:a.number,title:a.title,reason:kC(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:Ft(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Ut()}}async function jf(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Xo("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Ft(l)}`)}await lC(e);let o=n||"";if((await Pt({cwd:e})).GH_TOKEN){let l=await _u().catch(()=>null);l&&(o=`${o?`${o}
|
|
136
|
-
|
|
137
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await
|
|
108
|
+
`});function d$(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 p$(e,t,n){let r=yy(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function Zn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,f=(y,w)=>{u.set(y,w),t.push(w)},d=y=>{let w=u.get(y);w&&(w.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let y of e||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let w=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let v of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let $=v[0].split("/").pop().toLowerCase();m.has($)||m.set($,[]),m.get($).push(y.payload?.ts??y.ts)}}let h=3*6e4,g=20*6e4,x=(y,w)=>!!(y?.name&&m.get(String(y.name).toLowerCase()))?.some($=>$>=w-h&&$<=w+g);for(let y of e||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let w=Ne(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=Ne(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,R=null,M=new Map,N=new Map,C=new Map,T=new Map,j=new Map,Z=new Map,fe=new Map,ie=new Map,D=new Map,I=new Map,F=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,W=6e4,J=new Map,ee=(y,w)=>{let v=o.get(y);if(!v?.length)return!1;let $=Ne(w),ve=s.get(y);return v.some(b=>{if(!$.startsWith(b))return!1;let P=Ne($.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||Rc(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"&&dy(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:py(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:de(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${de(y)}`)||y.source==="transcript"&&ee(y.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){t.push({id:y.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:de(y),box:y.boxId,agent:y.provider});break}if(w.kind==="mock-picked"||w.kind==="ci-fix"||w.kind==="reconcile"||w.kind==="pr-go"||w.kind==="push-go"||w.kind==="spin-up-view"||w.kind==="prompt-answer"){w.kind==="prompt-answer"&&(le=null);break}for(let v of M.values())v.status="stale";M.clear();for(let v of C.values())v.status="stale";C.clear(),le&&le.box===y.boxId&&(t.push(d$(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=Ig(w,{codexFailedText:Dr});if(v){t.push({id:y.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:y.provider,legacy:!0,at:de(y),ts:de(y),box:y.boxId,agent:y.provider});break}He();let $=(w.images||[]).filter(Ce=>!x(Ce,de(y)));if(!String(w.text||"").trim()&&!$.length)break;if(y.provider==="cursor"){let Ce=`${y.boxId}|${w.text}`;if(n.has(Ce))break;n.add(Ce)}(!le||le.box===y.boxId)&&(le=null);let ve=y.provider==="codex"?(w.tools||[]).filter(Ce=>!wd.has(Ce)&&!la.has(Ce)):w.tools||[],b=w.text,P=null;if(Bd.has(y.provider)){let Ce=jd(String(w.text||""));Ce&&(b=Ce.text,P={questions:Ce.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:Ld(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<=W)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(xt(y,"lead-handoff"));break;case"subtask.delegated":t.push({...xt(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":t.push(xt(y,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let $ of M.values())$.status="stale";M.clear();for(let $ of C.values())$.status="stale";C.clear()}let v=xt(y,`pr-${w.change}`);if(v.box=y.boxId??null,f$.has(w.change)&&w.number!=null){let $=p$(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 $=yy(w),ve=R&&!R.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){(!R||R.complete)&&(R={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(R)),R.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)}),R.remaining=[...k.values()],R.complete=k.size===0;break}}t.push(v);break}case"pr.offered":{if(!w.offerId)break;for(let $ of M.values())$.status="stale";M.clear();for(let $ of C.values())$.status="stale";C.clear();let v={id:y.id,kind:"pr-offer",offerId:w.offerId,excerpt:w.excerpt||null,branch:w.branch||null,base:w.base||null,offered:Array.isArray(w.offered)&&w.offered.length?w.offered:null,toPr:w.toPr&&typeof w.toPr=="object"?{number:w.toPr.number??null}:null,available:Array.isArray(w.available)?w.available:null,defaults:Array.isArray(w.defaults)?w.defaults:null,prState:w.prState||null,openPr:w.openPr&&typeof w.openPr=="object"?w.openPr:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:de(y),box:y.boxId,agent:y.provider};M.set(w.offerId,v),N.set(w.offerId,v),t.push(v);break}case"pr.offer.answered":{let v=N.get(w.offerId);v&&(v.status=w.action||"dismiss",v.actions=Array.isArray(w.actions)?w.actions:null,v.notify=w.notify===!0,w.existingPr&&typeof w.existingPr=="object"&&(v.existingPr={number:w.existingPr.number??null}),M.delete(w.offerId));break}case"pr.exists.offered":{if(!w.offerId)break;for(let $ of C.values())$.status="stale";C.clear();let v={id:y.id,kind:"pr-exists-offer",offerId:w.offerId,srcOfferId:w.srcOfferId??null,branch:w.branch||null,base:w.base||null,pr:w.pr&&typeof w.pr=="object"?{number:w.pr.number??null,url:w.pr.url??null,title:w.pr.title??null}:null,actions:Array.isArray(w.actions)&&w.actions.length?w.actions:null,notify:w.notify===!0,status:"open",ts:de(y),box:y.boxId,agent:y.provider};C.set(w.offerId,v),T.set(w.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=T.get(w.offerId);v&&(v.status=w.choice||"dismiss",C.delete(w.offerId));break}case"box.state.changed":{let v=w.change==="model"?"model-change":w.change;if(!v)break;let $=xt(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(xt(y,"track-moved"));break;case"view.state.changed":{if(w.change==="deleted"){d(`view:${w.viewId}`);break}if(w.change!=="created"||w.restarted)break;f(`view:${w.viewId}`,{id:y.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:de(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{t.push({id:y.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:de(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(w.change==="picked"){t.push({id:y.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:de(y),box:y.boxId,agent:y.provider});break}if(w.change==="deleted"){d(`mock:${w.mockId}`);break}if(w.change!=="created")break;f(`mock:${w.mockId}`,{id:y.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(w.change==="deleted"){d(`flow:${w.flowId}`);break}if(w.change!=="created")break;f(`flow:${w.flowId}`,{id:y.id,kind:"flow-card",flowId:w.flowId,name:w.name,stepCount:w.stepCount??null,mockIds:w.mockIds??[],ts:de(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(w.change==="deleted"){d(`runbook:${w.runbookId}`);break}if(w.change!=="created")break;f(`runbook:${w.runbookId}`,{id:y.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:de(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{t.push({id:y.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!w.offerId)break;for(let $ of fe.values())$.box===(w.box||y.boxId)&&($.status="stale",fe.delete($.offerId));let v={id:y.id,kind:"fullscreen-offer",offerId:w.offerId,status:"open",ts:de(y),box:w.box||y.boxId,agent:w.agent||y.provider};fe.set(w.offerId,v),ie.set(w.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=ie.get(w.offerId);v&&(v.status=w.action==="restarted"?"restarted":w.action==="cleared"?"cleared":"dismiss",fe.delete(w.offerId));break}case"ticket.move.offered":{if(!w.offerId)break;for(let $ of j.values())$.key===w.key&&($.status="stale",j.delete($.offerId));let v={id:y.id,kind:"ticket-offer",offerId:w.offerId,provider:w.provider||"linear",key:w.key,url:w.url??null,phase:w.phase??null,from:w.from??null,to:w.to?.name??null,prNumber:w.prNumber??null,status:"open",ts:de(y),box:y.boxId,agent:y.provider};j.set(w.offerId,v),Z.set(w.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=Z.get(w.offerId);v&&(v.status=w.action==="moved"?"moved":"dismiss",j.delete(w.offerId));break}case"ticket.state.changed":{for(let v of j.values())v.key===w.key&&v.to===w.to&&(v.status="stale",j.delete(v.offerId));if(w.mode==="offer")break;t.push(xt(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(xt(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),I.set(w.proposalId,v),t.push(v);break}case"ticket.create.answered":{let v=I.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 F.values())$.status="stale";F.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};F.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",F.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?I.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(xt(y,w.kind));break}case"provider.error":t.push({...xt(y,"provider-error"),provider:y.provider,box:y.boxId,agent:y.provider});break;case"usage.cap.hit":t.push(xt(y,"usage-cap-hit"));break;case"usage.cap.warning":t.push(xt(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&&Ne(We.question)===Ne(b.question));P>=0?$.questions[P]=b:$.questions.push(b),$.questions.sort((We,Ht)=>We.index-Ht.index),$.questionTotal=Math.max($.questionTotal,w.questionTotal??1),$.questions.length>=$.questionTotal&&(t.push($),Se.delete(v));break}default:break}}let Xe=u.size?t.filter(y=>!y.retired):t;return h$(Xe.sort((y,w)=>(y.ts??0)-(w.ts??0)))}function h$(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<=m$){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 Hd(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:w$(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 kg(n,r)}function w$(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==No(e.command)?t:Jn(e.command,e.description||"")}function xt(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 by(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)+g$,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&y$.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"||!k$.has(r.type)||!r.box)continue;let o=(r.ts??0)+b$,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 xy(e){return(e||[]).filter(t=>x$.has(t?.kind)&&t.status==="open")}var de,f$,yy,m$,g$,y$,b$,k$,x$,Wd=O(()=>{Po();Br();gc();Od();Dd();Ud();de=e=>e.payload?.ts??e.ts;f$=new Set(["opened","ci-fixed","reconciled","pushed"]),yy=e=>`${e.repo??""}#${e.number}`;m$=2*6e4;g$=12e4,y$=new Set(["archived","unarchived","opened"]);b$=2500,k$=new Set(["pr-opened","pr-merged","pr-auto-armed"]);x$=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","fullscreen-offer"])});function E$(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 I$(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 R$(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<T$&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<A$&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Ay(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=E$(e),s=I$(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Zn(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-S$);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Ty[r]??Ty.failover),l.push(""),o.lastPush?.sha){let d=vy(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=vy(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=R$(e??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${_$(d.text,v$)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
|
|
109
|
+
`);return f.length<=Sy?f:`${f.slice(0,Sy)}
|
|
110
|
+
|
|
111
|
+
[\u2026 brief truncated]`}var S$,v$,Sy,T$,A$,_$,vy,Ty,Ey=O(()=>{Wd();S$=12,v$=2e3,Sy=48e3,T$=30,A$=10;_$=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},vy=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Ty={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 $$ from"node:fs";import Qn from"node:fs/promises";import er from"node:path";function My(e){let t=Cr(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=_y?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,_y).toString("utf8").replace(/�+$/,"")}
|
|
112
|
+
|
|
113
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function L$(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Ry.test(s)){if(r.length>=Ly)continue;let a=j$(s.replace(Ry,"").replace(/[.;,:]+$/,""),Fy,D$);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>Iy&&(o=`${o.slice(0,Iy-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Uy(e,t,{timeoutMs:n=9e4}={}){let r=er.join(t,N$),o=er.join(r,P$);await Qn.mkdir(r,{recursive:!0});let s=await $c(o),a=await se(e);if(!a)throw new Error(`session "${e}" not found`);return await Oc(e,a,B$(o)),await zy(o,s,n),L$(await Qn.readFile(o,"utf8"))}function Hy(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Py||n===jy;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(Ny)||n===O$||r.startsWith(Dy)||r.startsWith(By)}function Wy(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-F$;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 qy(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 Ky(e,{windows:t=[]}={}){if(Hy(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 Cc(e){return qd.get(e)||null}async function Gy(e){let t=qd.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await se(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:er.join(n.cwd,Oy,Cy)};return qd.set(e,r),W$(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function W$(e,t){e.step="summarize",await Vy(e.name,t),e.step="clear";let n=await se(e.name);await Oc(e.name,n,"/clear"),await Vd(U$),e.step="resume",await Oc(e.name,n,M$),e.step="done",e.finishedAt=Date.now()}async function Vy(e,t,{timeoutMs:n=H$}={}){let r=er.join(t,Oy),o=er.join(r,Cy);await Qn.mkdir(r,{recursive:!0});let s=await $c(o),a=await se(e);await Y({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Oc(e,a,C$(o)),await zy(o,s,n),await Y({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await $c(o)});let i=await ty(t,o,{exclude:u=>Hy(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:{...My(u),ts:Date.now()}})}catch{}}await Gu(t);let l=await q$(r,o);return c&&await zw(c,l||o,{box:e,agent:a?.agent??null}),o}async function Kd(e,{timeoutMs:t}={}){let n=await se(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return Vy(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Gd(e,t,{trackName:n="this Track"}={}){let r=await L({id:e,cwd:t}),o=Ay(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=er.join(t,$y);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:{...My(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),$y}async function $c(e){try{return(await Qn.stat(e)).mtimeMs}catch{return 0}}async function zy(e,t,n){let r=Date.now()+n;for(;;){if(await $c(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Vd(2e3)}}async function q$(e,t){try{if(!$$.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 Vd(e){return new Promise(t=>setTimeout(t,e))}async function Oc(e,t,n){if(t){let o=t.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":t.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":t.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await Xn({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await se(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await $e(e,["Escape"],{kind:"drive-wipe"}),await Vd(150),await $o(e,mc,{kind:"drive-wipe"}),await Fe(e,n,{kind:"orchestration-drive",...Gt(r.agent)})}var Oy,Cy,_y,Ny,Py,Dy,O$,C$,M$,By,jy,N$,P$,Iy,Ly,Fy,D$,B$,Ry,j$,F$,U$,H$,qd,$y,zd=O(()=>{tt();Tc();un();ha();ny();zs();ue();ac();Ey();Oy=".bostrat/handoff",Cy="latest.md",_y=32*1024;Ny="Checkpoint: write a work-in-progress handoff",Py="HANDOFF WRITTEN",Dy="Resume from .bostrat/handoff/latest.md",O$="/clear",C$=e=>`${Ny} 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 ${Py} and take no further action.`,M$=`${Dy}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,By="Beat: write a",jy="BEAT WRITTEN",N$=".bostrat/arc",P$="beat.txt",Iy=200,Ly=4,Fy=5,D$=36,B$=e=>`${By} 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 ${Ly} further lines, each starting with "- " and naming ONE piece in at most ${Fy} 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 ${jy} and take no further action.`,Ry=/^(?:[-–—*•·]|\d+[.)])\s+/,j$=(e,t,n)=>{let r=e.split(/\s+/).filter(Boolean).slice(0,t).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};F$=6e5;U$=3500,H$=3e5,qd=new Map;$y=".bostrat/handoff/status-latest.md"});function Yy(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!K$.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 G$(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:Yy(o.from,"from",n),toMs:Yy(o.to,"to",n)}))}return t}function z$(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 J$(e){if(!e)return null;let t=String(e).trim();return Jd[t]?t:Jy[t]?Jy[t]:Object.keys(Jd).find(r=>t.startsWith(r))||null}function Xy(e,t){let n=J$(e);if(!n)return null;let r=z$(t);return r===null?null:V$[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function tr(e,t={},n){let r=Xy(e,n);return r?((t.input||0)*r.input+(t.output||0)*r.output+(t.cacheWrite||0)*r.cacheWrite+(t.cacheRead||0)*r.cacheRead)/1e6:0}var Jd,Jy,K$,V$,Zy,xa=O(()=>{Jd={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Jy={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"},K$=/^\d{4}-\d{2}-\d{2}$/;V$=G$(Jd);Zy=(e,t)=>Xy(e,t)!==null});function Y$(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function eb(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function pn(e){let t=e.payload?.model||"unknown",n=Y$(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.source==="vendor",s=r,a=!1;if(n&&!o){let l=tr(t,n,e.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!Zy(t,e.ts);return{usd:s,model:t,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function nr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=Qy();for(let d of n){let{usd:p,model:m,tokens:h,repriced:g,unpriced:x,asRecorded:k}=pn(d);i+=k;let A=d.provider||"unknown",R=d.boxId||"unattributed";if(g&&c++,x&&(l++,u.add(m)),a+=p,eb(f,h),r[m]||={usd:0,tokens:Qy(),events:0},r[m].usd+=p,r[m].events+=1,eb(r[m].tokens,h),o[A]||={usd:0,events:0},o[A].usd+=p,o[A].events+=1,s[R]||={usd:0,events:0,provider:A,models:new Set,label:t[R]||R},s[R].usd+=p,s[R].events+=1,m!=="unknown"&&s[R].models.add(m),d.epoch!=null){let M=(s[R].epochs||={})[d.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:Bo(a),asRecordedUsd:Bo(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Mc(r,d=>({...d,usd:Bo(d.usd)})),byProvider:Mc(o,d=>({...d,usd:Bo(d.usd)})),byBox:Mc(s,d=>({usd:Bo(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Mc(d.epochs,p=>({usd:Bo(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Qy,Bo,Mc,Fr=O(()=>{xa();Qy=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});Bo=e=>Math.round(e*1e4)/1e4,Mc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function X$(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 tb(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,p]=f.split("#");return`${d.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Yd(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Yd(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${Yd(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=nr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?X$(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Yd,nb=O(()=>{Fr();Yd=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as Z$}from"node:child_process";import{promisify as Q$}from"node:util";import{randomUUID as eO}from"node:crypto";async function Xd(e,t){let n=Date.now()+t;for(;;){if(await dn(e))return!0;if(Date.now()>n)return!1;await Sa(nO)}}async function oO(e,t){let n=ob[t]||ob.default;n.escape&&(await $e(e,["Escape"],{kind:"swap-exit"}),await Sa(150)),n.wipe&&await $o(e,mc,{kind:"swap-exit"}),await hc(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Gt(t).submitDelayMs||0;await Sa(Math.max(r,250)),await $e(e,["Enter"],{kind:"swap-exit"})}async function sb(e,t){try{let{stdout:n}=await Ur(U,K("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ab(e){await Ur(U,K("respawn-pane","-k","-t",e))}async function Zd(e,t={}){if(!_e(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await se(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||Gn(n),a=Number(t.exitTimeoutMs)||tO;await Y({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await dn(e)&&(await oO(e,o),!await Xd(e,a)&&(i=!0,await Y({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await ab(e),!await Xd(e,rb))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Mr(n),l=c?Ui():null,u=await sb(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Ur(U,K("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Ur(U,K("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?cc(g,l):g}if(f&&!i&&(await ab(e),!await Xd(e,rb)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,p=t.resumeSessionId||eO(),m=d?n:nd(n,p,e),h=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Ln(r.cwd),s==="codex"&&await go(r.cwd),s==="cursor"&&await yo(r.cwd),await Nr(e),await Ur(U,K("set-option","-t",e,"@bostrat-sid",p)),await Ur(U,K("set-option","-t",e,"@bostrat-agent",s)),await Sa(rO),c&&na().length&&(await sb(e,ta)||await Ur(U,K("set-environment","-t",e,ta,"1")).catch(()=>{}),await Fe(e,`export ${ta}=1`,{kind:"swap-env"}),await Sa(150)),await Fe(e,m,{kind:"swap-launch"}),await Y({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:f,launchCmd:m}}var Ur,Sa,tO,rb,nO,rO,ob,ib=O(()=>{tt();Us();Wi();qi();ha();un();Ur=Q$(Z$),Sa=e=>new Promise(t=>setTimeout(t,e)),tO=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,rb=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,nO=250,rO=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 cb(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await it(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
114
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let p=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let p=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(p?No(p):null),detail:p?No(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&sO.test(aO(d.content))&&s.add(d.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var sO,aO,lb=O(()=>{Mt();Po();sO=/^Async (?:agent|task) launched/i,aO=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 rr(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 iO(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 ub(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:Nt(`read ${rr(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?rr(t.path,n):null,s=t?.glob?` --glob ${Qd(t.glob)}`:"",a=Nt(String(r),40);return{command:Nt(`grep ${Qd(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?rr(t.target_directory,n):null;return{command:Nt(`glob ${Qd(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Nt(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Nt(`edit ${rr(t.path,n)}`,140),description:`Edit ${jo(t.path)}`}:null;case"Write":return t?.path?{command:Nt(`write ${rr(t.path,n)}`,140),description:`Write ${jo(t.path)}`}:null;case"Delete":return t?.path?{command:Nt(`delete ${rr(t.path,n)}`,140),description:`Delete ${jo(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>rr(s,n)).filter(Boolean);return{command:Nt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=iO(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Nt(`apply_patch ${rr(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:Nt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Nc,Nt,jo,Qd,db=O(()=>{Nc=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Nt=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,jo=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);Qd=e=>`"${String(e).replace(/"/g,'\\"')}"`});import va from"node:fs";import cO from"node:fs/promises";import Hr from"node:path";import lO from"node:os";import uO from"node:crypto";import dO from"node:zlib";function mb(e){let t=uO.createHash("md5").update(String(e)).digest("hex");return Hr.join(fO,t)}function hb(e){try{let t=JSON.parse(va.readFileSync(Hr.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 pO(e){let t=mb(e),n;try{n=va.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Hr.join(t,o.name),a=Hr.join(s,"store.db");if(!va.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 Pc(e,t){if(!e)return null;if(t){let n=Hr.join(mb(e),t),r=Hr.join(n,"store.db");if(va.existsSync(r))return{id:t,dir:n,store:r,...hb(n)||{createdAtMs:null,updatedAtMs:null}}}return pO(e)}function fb(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function mO(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=dO.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function hO(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 wO(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Nc.has(r.toolName))continue;let o=ub(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 ef(e,t){if(!pb)return Lo;let{DatabaseSync:n}=pb,r;try{r=new n(e,{readOnly:!0})}catch{return Lo}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 Lo;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Lo}let a=s.lastUsedModel||null,i=s.latestRootBlobId?fb(r,s.latestRootBlobId):null;if(!i)return{...Lo,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of hO(i)){let m=mO(fb(r,p));if(!m)continue;let h=m.providerOptions?.cursor;if(h?.requestId&&d.push(h.requestId),h?.modelProviderMessageId&&d.push(h.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let k of m.content){let A=k?.providerOptions?.cursor?.modelName;A&&(f=A)}l.push(...wO(m.content,t).map((k,A)=>({...k,id:k.id||`${p}:${A}`}))),u.push(...yO(m.content));let g=gO(m.content);if(!g)continue;let x=[...new Set(u)];u=[],c.push({id:p,text:g,tools:x.length?x:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Lo}finally{try{r.close()}catch{}}}function wb(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(va.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 gb(e,t,{limit:n=80}={}){let r=Pc(e,t);if(!r)return[];let{turns:o,model:s}=await ef(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 yb(e,t){let n=Pc(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ef(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 bb(e,t,{limit:n=80}={}){let r=Pc(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await ef(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,f)=>{for(let d of u.commands)l.push({...d,summary:Jn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:Jn(u.command,u.description),ts:i+1});return l}async function kb(e,t){let n=Pc(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await cO.stat(n.store)).mtimeMs)}catch{return null}}var fO,gO,yO,pb,Lo,tf=O(()=>{Po();db();fO=process.env.BOSTRAT_CURSOR_CHATS||Hr.join(lO.homedir(),".cursor","chats");gO=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(`
|
|
115
|
+
`).trim():"",yO=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],pb=process.getBuiltinModule?.("node:sqlite"),Lo={turns:[],trailingCommands:[],model:null,requestIds:[]}});import bO from"node:fs/promises";function sf(e){return xb.set(e.agent,e),e}function Ta(e){return xb.get(e)||kO}async function cf(e,t={}){return(await Ta(e.agent).readMessages(e,t)).map(af(e))}async function Sb(e,t={}){return(await Ta(e.agent).readCommands(e,t)).map(af(e))}async function vb(e,t={}){return(await Ta(e.agent).readJobs(e,t)).map(af(e))}var Fo,nf,rf,of,kO,xb,af,Tb=O(()=>{Mt();yc();Po();lb();tf();Br();Fo=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}},nf=class extends Fo{transcript(t){return!t?.cwd||!t?.sessionId?null:Ct(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?yd(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?bg(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?cb(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await bO.stat(n)).mtimeMs)}catch{return null}}},rf=class extends Fo{async readMessages(t,n){return gb(t.cwd,t.sessionId,n)}async readCommands(t,n){return bb(t.cwd,t.sessionId,n)}async updatedAt(t){return kb(t.cwd,t.sessionId)}},of=class extends Fo{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 Tg(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Ag(t.cwd,this.sessionId(t))}},kO=new Fo("unknown"),xb=new Map;sf(new nf("claude",pd));sf(new rf("cursor",Nc));sf(new of("codex",la));af=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Kr(e){let t=e.search(ff);return(t>=0?e.slice(0,t):e).trim()}function xO(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function SO(e){let t=String(e).trim();return!!(!t||qr.test(e)||ff.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function vO(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=TO(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function TO(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(d=>d.length>3&&n.includes(d)).length;if(u)return u;let f=l.slice(0,8).trim();return f.length>=4&&n.includes(f)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function Eb(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function AO(e,t=""){let n=String(e);if(!Ib.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
116
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let f=r[u].match(Wr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!qr.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:Kr(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 _O(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
117
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Rb.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(Wr);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&&!(EO.test(n[c])||qr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Kr(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 IO(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
118
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Wr);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 $O(e,t=""){let n=String(e);if(!lf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
119
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(RO.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let f=r[u].match(Wr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!qr.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>lf.test(u.label)))return null;let i=s.map(u=>({label:Kr(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 pf(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(Wr);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 OO(e,t=""){let n=String(e);if(!$b.test(n)||!Ob.test(n)||Gr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
120
|
+
`),o=pf(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:Kr(s.label),description:""}))}]}:null}function CO(e,t=""){let n=String(e);if(!Cb.test(n)||Gr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
121
|
+
`),o=pf(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Kr(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 NO(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!MO.test(o)||mf.test(e[r]))break;n+=o}return n}function PO(e,t=""){let n=String(e);if(!mf.test(n)||Gr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
122
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=NO(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 BO(e,t=""){let n=String(e);if(!uf.test(n)||!DO.test(n)||Gr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
123
|
+
`)].reverse().find(a=>uf.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 jO(e,t=""){let n=String(e);if(!Mb.test(n)||Gr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
124
|
+
`),o=pf(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:Kr(s.label),description:""}))}]}:null}function LO(e,t=""){let n=String(e);if(Gr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
125
|
+
`),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(Nb);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(qr.test(r[l])||ff.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 FO(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(df.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 UO(e,t=""){let n=String(e);if(!_b.test(n)||!df.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
126
|
+
`),o=FO(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let T=i;T<c;T++){let j=r[T].match(Wr);j&&(u<0&&(u=T),l.push({line:T,cursor:!!j[1],checked:j[3]!=null&&j[3]!==" ",hasCheckbox:j[3]!=null,label:j[4].trim(),desc:""}))}if(!l.length)return null;for(let T of l)for(let j=T.line+1;j<Math.min(r.length,T.line+4)&&!(Wr.test(r[j])||qr.test(r[j]));j++){let Z=r[j].trim();if(!(!Z||SO(r[j])||Ab.test(Z))){T.desc=xO(Z);break}}let f=l.filter(T=>!Ab.test(T.label));if(!f.length)return null;let d=f.some(T=>T.hasCheckbox),p=Math.max(0,f.findIndex(T=>T.cursor)),m=s>=0?s+1:0,h=[];for(let T=m;T<u;T++){let j=r[T].trim();j&&!qr.test(r[T])&&!/[☐☒]/.test(r[T])&&h.push(j)}let g=h.join(" ")||(s>=0?Eb(r[s]):"")||"The agent is asking a question",x=s>=0?vO(r[s],g):null,k=x?.currentTabLabel||(s>=0?Eb(r[s]):""),A=f.map(T=>({label:Kr(T.label),description:T.desc})),R=A.map(T=>T.label).join("|"),N=`claude:${x?`q${x.questionIndex+1}/${x.questionTotal}`:"q1/1"}:${k}:${g}:${R}`.slice(0,240),C=x?`claude:ask:${x.tabs.map(T=>T.label).join("|")}`.slice(0,240):N;return{provider:"claude",box:t,multiSelect:d,cursorIndex:p,promptId:N,groupId:C,...x?{questionIndex:x.questionIndex,questionTotal:x.questionTotal,currentTabLabel:x.currentTabLabel,tabs:x.tabs}:{},questions:[{question:g,header:k,multiSelect:d,options:A}]}}function Vr(e,t){let n=String(t||"");return n?e==="claude"?_b.test(n)&&df.test(n)||Ib.test(n)||lf.test(n)||Rb.test(n.trimEnd().split(`
|
|
127
|
+
`).pop()||"")?!0:Gr.test(n)?!1:$b.test(n)&&Ob.test(n)||Cb.test(n)||mf.test(n)||uf.test(n)||Mb.test(n)||Nb.test(n.trimEnd().split(`
|
|
128
|
+
`).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 or(e,t){return!e||!t?null:e.agent==="claude"?IO(t,e.name)||UO(t,e.name)||AO(t,e.name)||$O(t,e.name)||BO(t,e.name)||PO(t,e.name)||CO(t,e.name)||OO(t,e.name)||jO(t,e.name)||LO(t,e.name)||_O(t,e.name):e.agent==="cursor"||e.agent==="codex"?Fg(e.agent,t,e.name):null}async function hf(e){if(!e)return null;let t=await fn(e.name).catch(()=>"");return or(e,t)}function Pb(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 Db(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 _b,df,Wr,Ab,qr,ff,Ib,Rb,EO,lf,RO,Gr,$b,Ob,Cb,mf,MO,uf,DO,Mb,Nb,wf=O(()=>{tt();kc();_b=/to navigate\b.*\bEsc to cancel|Enter to select\b/,df=/Chat about this/i,Wr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Ab=/^(type something|chat about this|submit)\b/i,qr=/^[─—-]{6,}\s*$/,ff=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;Ib=/^\s*Switch model\?\s*$/m;Rb=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,EO=/^\s*[─—▔-]{6,}\s*$/;lf=/Resume from summary/i,RO=/^\s*This session is\b/;Gr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;$b=/Choose the text style that looks best with your terminal/,Ob=/Let's get started\./;Cb=/Select login method:/;mf=/Paste code here if prompted/,MO=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;uf=/OAuth error:/,DO=/Press Enter to retry\./;Mb=/Quick safety check: Is this a project you created or one you trust/;Nb=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Dc(e,t,n){if(!e||!t)return null;for(let r of HO)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function qO(e){let t=gf.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},gf.set(e,t)),t}function Lb(e,t,n=Date.now()){let r=qO(e);if(r.sentAt=r.sentAt.filter(s=>n-s<jb),r.sentAt.length>=Bb)return{action:"give-up"};if(n-r.last<WO)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 Fb(e,t=Date.now()){let n=gf.get(e);return n?n.sentAt.filter(r=>t-r<jb).length>=Bb:!1}var HO,WO,Bb,jb,gf,yf=O(()=>{HO=[{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||""}
|
|
129
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];WO=5e3,Bb=5,jb=3600*1e3,gf=new Map});function Uo(e){return typeof e=="string"?e.trim():""}var Ub=O(()=>{});import{execFile as KO}from"node:child_process";import{promisify as GO}from"node:util";async function Re(e,t,{input:n}={}){let r=kf("git",["-C",e,...t],{maxBuffer:Hb});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Ho(e){if(!e)return!1;try{let{stdout:t}=await Re(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function Wb(e){let{stdout:t}=await Re(e,["rev-parse","--show-toplevel"]);return t.trim()}async function VO(e){try{return await Re(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Wo(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Re(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>kf("git",["-C",e,...a],{maxBuffer:Hb,env:o,...i});try{let a=await VO(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 kf("rm",["-f",r])}catch{}}}async function xf(e,t){let{stdout:n}=await Re(e,["status","--porcelain"]);return n.trim()?(await Re(e,["add","-A"]),await Re(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t]),!0):!1}async function Aa(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Re(e,["status","--porcelain"]),r=n.split(`
|
|
130
|
+
`).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 Ea(e,t){if(!t)return 0;try{let{stdout:n}=await Re(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function qo(e,t,n,r){return await Re(e,["worktree","add","--no-track","-b",n,t,r]),t}async function qb(e,t){try{return await Re(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function Bc(e,t,n){return await Re(e,["worktree","add",t,n]),t}async function Ko(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Re(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 Re(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Sf(e){await Re(e,["init","-q"]),await Re(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 Re(e,["worktree","remove","--force",t])}catch{try{await Re(e,["worktree","prune"])}catch{}}}function zO(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 vf(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Tf(e,t,n,r){let{stdout:o}=await Re(e,vf(["diff","--shortstat",t,n],r));return zO(o)}async function Af(e,t,n,r){let{stdout:o}=await Re(e,vf(["diff","--name-only",t,n],r));return o.split(`
|
|
131
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Ef(e,t,n,r){let{stdout:o}=await Re(e,vf(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await bf(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await bf(e,o,["--3way","--whitespace=nowarn"]),a=await JO(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await bf(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function bf(e,t,n){try{return await Re(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function JO(e){try{let{stdout:t}=await Re(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
132
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var kf,Hb,jc=O(()=>{kf=GO(KO),Hb=64*1024*1024});import _f from"node:fs/promises";import Gb from"node:path";import YO from"node:os";function Yb(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function Xb(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function Zb(e){let t=new Date(Yb(e));return t.setDate(t.getDate()+7),t.getTime()}function Qb(e){let t=new Date(Xb(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Fc(e=Date.now()){for(let[t,n]of Object.entries(Go)){let r=n.startOf(e);if(r!==mn[t]){mn[t]=r;for(let o of zr.values())o[t]=0;for(let o of Vo)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Vo.delete(o)}}}function ek(e,t){let n=Pt?.caps?.[e]?.[Go[t].capKey];return Number.isFinite(n)&&n>0?n:null}function Jr(e,t=Date.now()){if(!e||!Rf||!Pt?.caps)return null;Fc(t);let n=zr.get(e);for(let r of Object.keys(Go)){let o=ek(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Lc(s),resetsAt:Go[r].nextStart(t)}}return null}function tk(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${mn[t.window]}`;return Vo.has(n)?!1:(Vo.add(n),!0)}function Kb(e,t,n,r=Date.now()){if(!e||!t)return;Fc(r);let o=XO(e);for(let[s,a]of Object.entries(Go))n>=mn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function ZO(e,t,n=Date.now()){if(!Pt?.caps||!If)return;let r=zr.get(e);if(r)for(let[o,s]of Object.entries(Go)){let a=ek(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*Jb||i>=a)continue;let c=`warn:${e}:${o}:${mn[o]}`;if(!Vo.has(c)){Vo.add(c);try{let l=await If(t);if(!l)continue;await _(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Lc(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function nk(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 Uc(e,{source:t="local",updatedAt:n=Date.now()}={}){Pt={caps:nk(e),updatedAt:n,source:t};try{await _f.mkdir(Vb,{recursive:!0}),await _f.writeFile(zb(),JSON.stringify(Pt,null,2))}catch{}return _a()}function _a(e=Date.now()){Fc(e);let t=new Set([...zr.keys(),...Object.keys(Pt?.caps||{})]),n={};for(let r of t){let o=zr.get(r);n[r]={weeklyUsd:Lc(o?.weekly||0),monthlyUsd:Lc(o?.monthly||0)}}return{caps:Pt?.caps||{},configured:!!Pt,seeded:Rf,updatedAt:Pt?.updatedAt??null,spent:n,windows:{weekly:{start:mn.weekly,resetsAt:Zb(e)},monthly:{start:mn.monthly,resetsAt:Qb(e)}},warnPct:Jb}}async function rk({listTracks:e,getTrack:t}={}){If=t||null,Fc();try{let o=JSON.parse(await _f.readFile(zb(),"utf8"));Pt={caps:nk(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{Pt=null}let n=Date.now();st.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=pn(o);Kb(o.provider||"unknown",s,o.ts??Date.now()),ZO(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(mn.weekly,mn.monthly);for(let o of await e?.()||[]){let s;try{s=await L(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||Kb(a.provider||"unknown",pn(a).usd,i,n)}}return Rf=!0,_a()}var Vb,zb,Jb,Go,Pt,Rf,mn,zr,Vo,If,XO,Lc,Hc=O(()=>{ue();Fr();Vb=process.env.BOSTRAT_STATE_DIR||Gb.join(process.env.HOME||YO.tmpdir(),".bostrat"),zb=()=>Gb.join(Vb,"usage-caps.json"),Jb=.8;Go={weekly:{startOf:Yb,nextStart:Zb,capKey:"weeklyUsd"},monthly:{startOf:Xb,nextStart:Qb,capKey:"monthlyUsd"}},Pt=null,Rf=!1,mn={weekly:0,monthly:0},zr=new Map,Vo=new Set,If=null,XO=e=>{let t=zr.get(e);return t||(t={weekly:0,monthly:0},zr.set(e,t)),t};Lc=e=>Math.round(e*1e4)/1e4});import QO from"node:fs/promises";import eC from"node:os";import ok from"node:path";function dk(e){return e==="codex"?oC:e==="cursor"?uk:ck}function sk(e){let t=String(e||"").split(`
|
|
133
|
+
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function ak(e){let t=String(e||"").split(`
|
|
134
|
+
`),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 sC(e){let t=String(e||"").split(`
|
|
135
|
+
`);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 ik(e){let t=String(e||"").split(`
|
|
136
|
+
`),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 fk(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&&!lk.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=uk.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!rC.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ia(e,t,{timeoutMs:n=tC}={}){let r=Date.now()+n;for(;;){let o=await Ee(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Wc(nC)}}async function aC(e,t){return await Fe(e,`/model ${t.model}`,Gt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function iC(e,t){await pc(e),await Fe(e,"/model",{enter:!1}),await Wc(250),await $e(e,["Enter"],{kind:"model-switch"});let n=await Ia(e,sk);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await $e(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=sC(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Fe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ia(e,i=>ak(i)||!sk(i)&&o(i)&&{done:o(i)});if(!s)throw await $e(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?lk.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await $e(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Fe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ia(e,i=>!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 cC(){let e=ok.join(process.env.CURSOR_CONFIG_DIR||ok.join(eC.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await QO.readFile(e,"utf8"))?.model||null}catch{return null}}async function lC(e,t){await pc(e),await hc(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ia(e,ik),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(t.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await Ia(e,ik,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await $e(e,["Down"],{kind:"model-switch"}),await Wc(250)}if(!n||!s||!o(s)){await $e(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await $e(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await $e(e,["Enter"],{kind:"model-switch"}),await Wc(800);let a=await cC();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 pk(e,t,n){return e==="codex"?iC(t,n):e==="cursor"?lC(t,n):aC(t,n)}var Wc,tC,nC,ck,rC,oC,lk,uk,mk=O(()=>{tt();ha();Wc=e=>new Promise(t=>setTimeout(t,e)),tC=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,nC=300,ck=[{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"}],rC=new Set([...ck.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"]),oC=[{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"}],lk=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),uk=[{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 Kc(e){let t=e||{},n=uC(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function uC(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:ct(e.agent),payload:{...sr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:ct(e.agent||e.toAgent),payload:{kind:"subtask-failed",...sr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"subtask-resumed",...sr(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...sr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...sr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...sr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"rehydrated"}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{...sr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...sr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function gk(e,t){let n=Kc(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Of(()=>_(r,n))}async function yk(e){if(!(!e?.id||hk.has(e.id))){hk.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=Kc(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await _(e,o)}}catch{}}}function sr(e){let{type:t,at:n,...r}=e;return r}function bk(e,t,n){!n?.promptId||$f.get(e.id)===n.promptId||($f.set(e.id,n.promptId),Of(()=>_(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:ct(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function Cf(e,t,n,r={}){$f.delete(e.id),Of(()=>_(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var ct,qc,Of,wk,hk,$f,Mf=O(()=>{ue();ct=e=>e||null;qc=Promise.resolve(),Of=e=>(qc=qc.then(e).catch(()=>{}),qc),wk=()=>qc;hk=new Set;$f=new Map});function hC(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Ak(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of hC(n).reverse()){if(!dC.test(r))continue;let o=r.endsWith("?")&&r.length<=mC&&pC.test(r);if(fC.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function _k(e){let t=String(e||""),n=[];wC.test(t)&&n.push("commit"),Ek.test(t)&&n.push("rebase"),gC.test(t)&&n.push("push");let r=bC.test(t);!r&&yC.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 Ik(e){return Ek.test(String(e||""))}function Jo(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}function Rk(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 $k(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<=kC&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Ok(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+xC||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Ck(e){let t=(Array.isArray(e)?e:[]).filter(r=>kk[r]).map(r=>kk[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 xk,Sk,vk,Tk,dC,fC,pC,mC,zo,wC,Ek,gC,yC,bC,kC,xC,kk,Yo=O(()=>{xk=/(?<!\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,Sk=/(?<!\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,vk=/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,Tk=/(?<!\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,dC=new RegExp(`\\b(?:${xk}|${Sk}|${vk}|${Tk})\\b`,"i"),fC=/\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,pC=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,mC=90;zo=["commit","rebase","push","pr"],wC=new RegExp(`\\b(?:${xk})\\b`,"i"),Ek=new RegExp(`\\b(?:${Tk})\\b`,"i"),gC=new RegExp(`\\b(?:${Sk})\\b`,"i"),yC=new RegExp(`\\b(?:${vk})\\b`,"i"),bC=/\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;kC=3e4;xC=2500;kk={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import SC from"node:crypto";import Gc from"node:fs/promises";import Mk from"node:path";import vC from"node:os";async function Oe(){return ar||(Nf??=Gc.readFile(Df,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(ar=e,!t&&Ae()){let{records:n,source:r}=await je("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;ar[Q(i.repo,i.number)]=i}Object.keys(ar).length&&await Bf().catch(()=>{})}}return ar}).finally(()=>Nf=null),Nf)}async function Bf(){let e=JSON.stringify({links:ar},null,2),t=`${Df}.${process.pid}.tmp`;await Gc.mkdir(Nk,{recursive:!0}),await Gc.writeFile(t,e),await Gc.rename(t,Df)}async function Ue(e){Pf=Pf.then(Bf,Bf),await Pf.catch(()=>{});let t=ar?.[e];t&&et("prlinks",{id:Pk(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 Oe(),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:TC.has(a),firstSeen:Date.now()},await Ue(Q(e,t)),{link:i[c],created:!0})}async function hn(e,t){if(!e||t==null)return null;let n=(await Oe())[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 Oe())[Q(e,t)]??null}async function ir(e,t,n){if(!e||t==null)return null;let r=(await Oe())[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 Dk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Oe())[Q(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Ue(Q(e,t)),o):null}async function Bk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Oe())[Q(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Ue(Q(e,t)),o):null}async function Vc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Oe())[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 zc(e,t){if(!e||t==null)return null;let n=(await Oe())[Q(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Ue(Q(e,t)),n)}async function Jc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Oe())[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 Yc(e,t){if(!e||t==null)return null;let n=(await Oe())[Q(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Ue(Q(e,t)),n)}async function jk(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Oe())[Q(e,t)];return r?(r.baseFoldAt=n,await Ue(Q(e,t)),r):null}async function Lk(e,t){if(!e||t==null)return null;let n=(await Oe())[Q(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ue(Q(e,t)),n)}async function Fk(e,t,n){if(!e||t==null||!n)return null;let r=(await Oe())[Q(e,t)];return r?(r.lastHeadSha=n,await Ue(Q(e,t)),r):null}async function Uk(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Oe())[Q(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Ue(Q(e,t))),{prev:o,link:r}}async function Ra(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Oe())[Q(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Ue(Q(e,t)),r):null}async function Yr(e,t,n=!0){if(!e||t==null)return null;let r=(await Oe())[Q(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Ue(Q(e,t)),r):null}async function $a(e,t,n=!0){if(!e||t==null)return null;let r=(await Oe())[Q(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Ue(Q(e,t)),r):null}async function Hk(e,t,n){if(!e||t==null)return null;let r=(await Oe())[Q(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Ue(Q(e,t)),r}async function Oa(e,t){if(!e||t==null)return null;let n=(await Oe())[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 Wk(){return Object.values(await Oe()).filter(e=>!AC.has(e.state))}async function Xr(){return Object.values(await Oe()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function qk(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Oe(),r=0;for(let[o,s]of Object.entries(n))await et("prlinks",{id:Pk(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Nk,Df,Q,Pk,ar,Nf,Pf,TC,AC,wn=O(()=>{Kn();Nk=process.env.BOSTRAT_STATE_DIR||Mk.join(process.env.HOME||vC.tmpdir(),".bostrat"),Df=Mk.join(Nk,"pr-links.json"),Q=(e,t)=>`${e}#${Number(t)}`,Pk=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=SC.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},ar=null,Nf=null,Pf=Promise.resolve();TC=new Set(["api","observed","reconciled"]);AC=new Set(["merged","closed","lost"])});import EC from"node:fs";import jf from"node:fs/promises";import zk from"node:path";import _C from"node:os";function Xk(e,t=!1){if(!t&&e-Gk<IC)return;Gk=e;let n=JSON.stringify({windowStart:Xo,byCaller:Object.fromEntries(gn),lastHour:cr});Vk=Vk.then(()=>jf.mkdir(Jk,{recursive:!0})).then(()=>jf.writeFile(`${Xc}.tmp`,n)).then(()=>jf.rename(`${Xc}.tmp`,Xc)).catch(()=>{})}function Zk(e){e-Xo<Yk||(cr={start:Xo,total:Zc(gn),byCaller:Object.fromEntries(gn)},cr.total&&console.log(`[github] ${cr.total} calls last hour`,cr.byCaller),Xo=e,gn=new Map,Xk(e,!0))}function yn(e="other",t=Date.now()){Zk(t),gn.set(e,(gn.get(e)||0)+1),Xk(t)}function Qk(e=Date.now()){return Zk(e),{windowStart:Xo,total:Zc(gn),byCaller:Object.fromEntries(gn),lastHour:cr,backoffUntil:Zr>e?Zr:null}}function lr(e){return RC.test(String(e?.message||e||""))}function ur(e,t=Date.now()){return Zr<=t&&console.warn(`[github] rate limited \u2014 backing off ${Kk/6e4} min:`,String(e?.message||e||"").slice(0,200)),Zr=Math.max(Zr,t+Kk),Zr}function Qc(e=Date.now()){return Math.max(0,Zr-e)}var Jk,Xc,Yk,Kk,IC,Xo,gn,cr,Zr,Gk,Zc,Vk,RC,Ca=O(()=>{Jk=process.env.BOSTRAT_STATE_DIR||zk.join(process.env.HOME||_C.tmpdir(),".bostrat"),Xc=zk.join(Jk,"github-metrics.json"),Yk=60*6e4,Kk=5*6e4,IC=6e4,Xo=Date.now(),gn=new Map,cr=null,Zr=0,Gk=0,Zc=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(EC.readFileSync(Xc,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Yk?(Xo=e.windowStart,gn=new Map(Object.entries(e.byCaller||{})),cr=e.lastHour||null):Zc(new Map(Object.entries(e.byCaller||{})))&&(cr={start:e.windowStart,total:Zc(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Vk=Promise.resolve();RC=/rate limit|too quickly|abuse detection|HTTP 429/i});import ex from"node:fs/promises";import tx from"node:path";import $C from"node:os";function tl({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};el.push(l),el.length>CC&&el.shift();let u=Lf.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)),Lf.set(e,u),MC(l),l}async function MC(e){try{await ex.mkdir(nx,{recursive:!0}),await ex.appendFile(OC,JSON.stringify(e)+`
|
|
137
|
+
`)}catch{}}function rx(){let e={};for(let[t,n]of Lf)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:el.slice(-20)}}var nx,OC,CC,el,Lf,nl=O(()=>{nx=process.env.BOSTRAT_STATE_DIR||tx.join(process.env.HOME||$C.tmpdir(),".bostrat"),OC=tx.join(nx,"github-latency.ndjson"),CC=100,el=[],Lf=new Map});import{execFile as NC}from"node:child_process";import{promisify as PC}from"node:util";async function DC(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Qo("git",["-C",e,"rev-parse","HEAD"]),Qo("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await _({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function St(e,t,n={}){let{caller:r="prs",...o}=n,s=await qt({cwd:e});yn(r);try{let{stdout:a}=await Qo("gh",t,{cwd:e,...BC,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw lr(a)&&ur(a),a}}async function Hf(){if(rl&&Date.now()-rl.at<6e4)return rl.data;let e;try{yn("auth");let t=await Qo("gh",["auth","status"],{timeout:1e4});e={installed:!0,authenticated:!0,login:`${t.stdout||""}${t.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(t){e=t?.code==="ENOENT"||t?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(e.installed&&!e.authenticated)try{let t=await qs();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return rl={at:Date.now(),data:e},e}async function jC(e){try{let{stdout:t}=await Qo("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function sx(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 Ff(e){let t=(e||[]).map(sx),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 qf(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function ix(e,t){if(!t)return null;let n=await me(e);if(!n)return null;try{let r=await St(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 cx(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 lx(e,t){let n=await ix(e,t);return n===null?{...Ff([]),unavailable:!0}:Ff(cx(n))}function ol(e,t=Ff(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 FC(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Zo.delete(s);continue}r.add(s),Zo.has(s)||Zo.set(s,n),o.mergeabilityPending=n-Zo.get(s)<LC}for(let o of Zo.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Zo.delete(o);return t}async function es(e,t){let n=await bn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await St(n.path,["pr","view",String(r),"--json",Wf]);return ol(JSON.parse(o))}catch(o){if(!qf(o))throw o;let s=await St(n.path,["pr","view",String(r),"--json",ax]),a=JSON.parse(s);return ol(a,await lx(n.path,a.headRefOid))}}async function Vt(e,{caller:t="prs"}={}){try{let n=await St(e,["pr","list","--json",Wf,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>ol(r)).sort(ox)}catch(n){if(!qf(n))throw n;let r=await St(e,["pr","list","--json",ax,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>ol(o,await lx(e,o.headRefOid))))).sort(ox)}}function Jt(){Ma.clear()}async function WC(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 Vc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Jc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function ux(){let e=await Hf();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await cn())await jC(a.path)&&t.push(a);let n=Date.now(),r=Qc(n)>0,o=await Promise.all(t.map(async a=>{let i=Ma.get(a.path),c=i?.pending?HC:UC;if(i&&(r||n-i.at<c))return i.entry;try{let l=await me(a.path).catch(()=>null),u=FC(a.path,await Vt(a.path,{caller:"tab"}),n);if(await WC(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&&tl({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return Ma.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:zt(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Ma.keys())s.has(a)||Ma.delete(a);return{auth:e,repos:o}}async function bn(e){let t=(await cn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Qr(e){return bo(e)}async function ts(e,t,{method:n="squash"}={}){let r=await bn(e);if(!Na.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(t);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await qt({cwd:r.path})).GH_TOKEN){let a=await Du().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await St(r.path,s)}catch(a){throw new Error(zt(a))}finally{Jt()}return{ok:!0,number:o}}async function Pa(e,t){let n=await bn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await St(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(zt(s))}finally{Jt()}let o=await me(n.path).catch(()=>null);return o&&await jk(o,r).catch(()=>{}),{ok:!0,number:r}}async function Kf(e,t){let n=await bn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await St(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(zt(o))}finally{Jt()}return{ok:!0,number:r}}function Gf(e){return e.green&&!e.isDraft}function VC(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 dx(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Gf(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function fx(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function px(){let e={};for(let[t,n]of Uf)e[t]={...n};return e}async function mx(e,{method:t="merge",onMerged:n=null}={}){let r=await bn(e);if(!Na.has(t))throw new Error(`invalid merge method: ${t}`);if(Uf.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Vt(r.path)).filter(Gf).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 Uf.set(r.path,s),JC(s,o,{listPrs:()=>Vt(r.path),merge:a=>ts(r.path,a,{method:t}),sleep:GC,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function zC(e,t){let n=Date.now()+(t.settleMaxMs??KC),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(qC)}}async function JC(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 zC(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(!Gf(a)){e.skipped.push({number:a.number,title:a.title,reason:VC(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:zt(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Jt()}}async function Vf(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Qo("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${zt(l)}`)}await DC(e);let o=n||"";if((await qt({cwd:e})).GH_TOKEN){let l=await Du().catch(()=>null);l&&(o=`${o?`${o}
|
|
138
|
+
|
|
139
|
+
`:""}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 St(e,s)}catch(l){throw new Error(zt(l))}finally{Jt()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function hx(e,t){let n=await bn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await St(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(!qf(i))throw i;let c=await St(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await ix(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(zt(i));o=cx(l).map(sx).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=YC(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await St(n.path,["run","view",c,"--log-failed"]).then(u=>XC(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function YC(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function XC(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
138
140
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
139
141
|
`+o.slice(-t).join(`
|
|
140
142
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
141
|
-
`+r.slice(-n)),r}function
|
|
142
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
143
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+VC(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 Uf(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function f0(e){let t=u0(e),n=t.match(qC);if(!n)return null;let r=Uf(t,n.index);return GC.test(r)?null:{repo:r.match(d0)?.[1]??null,head:r.match(zC)?.[1]??null}}function p0(e){let t=u0(e),n=t.match(KC);if(!n)return null;let r=Uf(t,n.index),o=Uf(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("-")){YC.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(d0)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function ZC(e,t,{exclude:n=()=>!1}={}){let r=async i=>{un("observer");let{stdout:c}=await a0("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",XC],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw tr(l)&&nr(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-UC&&!n(c)),s=await Pt({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 QC(e,t){if(t)return[t];let n=[];for(let r of m0(e)){let o=await de(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await en().catch(()=>[])){let o=await de(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function m0(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 eM(e,t,n){let r=await ns(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)||JC(c.headRefName,e.id),i=[];for(let c of await QC(e,t?.repo??null)){let l;try{l=await ZC(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await Ze(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===e.id&&f.announced)return{settled:!0};if(f&&f.trackId!==e.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==e.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function h0(e,t,{delays:n=i0,turnEnd:r}={}){let o=f0(t.command);if(!o)return null;let s=!!p0(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(rr.has(a))return null;rr.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 eM(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:m,created:p}=await ut({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!p&&!(m?.trackId===e.id&&await cn(f,d.number))?null:(await Ff(t.box,t.agent,r),await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{rr.delete(a)}})();return c.catch(()=>rr.delete(a)),c}function w0(e,t,{delays:n=i0,turnEnd:r,openedWait:o}={}){let s=p0(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(rr.has(a))return null;rr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let p=m0(e);if(p.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of p){let y=await de(w).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=(await ns(e).catch(()=>[])).filter(p=>p!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(p,w)=>{let y=await Ze(c,p.number);return y&&y.trackId!==e.id?null:(y||await ut({repo:c,number:p.number,trackId:e.id,url:p.url,headRef:p.headRefName,title:p.title,source:"merge-observed"}),await xa(c,p.number),w&&((await B(e).catch(()=>[])).some(R=>R.type==="pr.state.changed"&&(R.payload?.change==="auto-armed"||R.payload?.change==="merged")&&R.payload?.number===p.number)||(await Ff(t.box,t.agent,r),await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:c}}))),p)},m=null;for(let p of n){await new Promise(O=>setTimeout(O,p));let w=[];for(let O of f)try{un("observer");let{stdout:M}=await a0("gh",["pr","view",String(l),"--repo",O,"--json",tM],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Pt({slug:O})}}),T=JSON.parse(M||"null");T?.number&&w.push({slug:O,pr:T})}catch(M){tr(M)&&nr(M)}let y=w.find(O=>O.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let b=y.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(m=b,s.auto)return d(b,!0);continue}let v=await Ze(c,b.number);if(v&&v.trackId!==e.id)return null;v||await ut({repo:c,number:b.number,trackId:e.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await Ff(t.box,t.agent,r);let R=await B(e).catch(()=>[]);if(R.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="merged"&&O.payload?.number===b.number))return null;let A=O=>O.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="opened"&&M.payload?.number===b.number),V=0;if(!A(R)){if(f0(t.command)&&await cn(c,b.number)){let O=Date.now();V=O+1,await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:O,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Ze(c,b.number))?.announced){let O=o?.capMs??HC,M=o?.pollMs??WC,T=Date.now()+O;for(;Date.now()<T&&(await new Promise(q=>setTimeout(q,M)),!A(await B(e).catch(()=>[]))););}}return await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),V),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await pn(c,b.number).catch(()=>{}),await fn(c,b.number).catch(()=>{}),await Zn(c,b.number,"merged"),b}return m?d(m,!1):null}finally{rr.delete(a)}})();return i.catch(()=>rr.delete(a)),i}var a0,i0,LC,FC,UC,HC,WC,c0,l0,qC,KC,GC,s0,VC,d0,zC,JC,YC,XC,rr,tM,g0=C(()=>{le();Tr();Je();Ta();Qn();Jr();Ra();rs();a0=jC(DC),i0=[4e3,15e3,45e3],LC=2500,FC=5*6e4;UC=15e3,HC=9e4,WC=2e3,c0=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,l0=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,qC=new RegExp(`${c0}${l0}create\\b`),KC=new RegExp(`${c0}${l0}merge\\b`),GC=/\s--web\b/,s0=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,VC=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");d0=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,zC=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,JC=o0;YC=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);XC="number,url,title,headRefName,createdAt";rr=new Set;tM="number,url,title,headRefName,state"});import nM from"node:fs/promises";import Wf from"node:path";import rM from"node:os";function Hf(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function el(e){let t=oM.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Hf(n,"from"),s=o?o.replace(/^uds:/,""):null,a=y0(s);return{socket:s,pid:a,name:Hf(n,"from-name"),mode:Hf(n,"from-mode"),text:r.trim()}}function y0(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function sM(){return process.env.CLAUDE_CONFIG_DIR?Wf.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Wf.join(rM.homedir(),".claude","sessions")}async function aM(e){let t=y0(e);if(!t)return null;try{let n=await nM.readFile(Wf.join(sM(),`${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 b0(e,t){let n=await aM(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 oM,qf=C(()=>{oM=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function Kf(e=process.env){return!!(ge(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Gf({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Kf(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),k0);try{let a=await x0(`${ge(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 v0(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(!Kf(s))return{ok:!1,source:"unconfigured"};let i=tl.get(e.id)||0;if(a()-i<iM)return{ok:!1,source:"debounced"};tl.set(e.id,a());let c=await nl({env:s}),l=c.loops.find(d=>d.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),f=await Gf({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||tl.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function nl({env:e=process.env}={}){if(!Kf(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),k0);try{let r=await x0(`${ge(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 k0,x0,iM,tl,Vf=C(()=>{Je();k0=1e4,x0=(...e)=>fetch(...e);iM=10*6e4,tl=new Map});import{randomUUID as cM}from"node:crypto";function $a(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function S0(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function T0(e){return`job|${e.box}|id:${e.id}`}async function Jf(e){return zf.has(e.id)?zf.get(e.id):(rl.has(e.id)||rl.set(e.id,B(e).then(t=>{let n={keys:new Set,cmdN:0};for(let r of t)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add($a({role:o,sourceId:r.payload.sourceId})),n.keys.add($a({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($a({role:"user",sourceId:r.payload.sourceId})),n.keys.add($a({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(S0({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(T0({id:r.payload.id,box:r.boxId}));return zf.set(e.id,n),n}).finally(()=>rl.delete(e.id))),rl.get(e.id))}async function A0(e,t){let n=new Set;try{let r=(await Jf(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=$a(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?el(o.text):null;if(a){await I(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 b0(a,Oa),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=fg(o);if(i){await I(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}}),v0(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 I(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=lk(o.text);if(c){let l=fk(c),u;if(e.workspace?.kind==="multi"){let p=await os(e).catch(()=>[]);u=p.length?p.map(w=>({name:w.name,slug:w.repo??null})):void 0}let f=(e.workspace?.kind==="multi"?null:await ts(e.cwd).catch(()=>null))||e.workspace.branch,d=await Qk(e,{branch:f,rebase:l}).catch(()=>null),m=d?null:dk(c);if(d&&d.available.length===0)continue;await I(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:cM(),ts:o.ts,excerpt:c,branch:f,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:d?.available,defaults:d?.defaults,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||u,offered:d?.defaults||m?.offered||void 0,toPr:m?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function E0(e,t){try{let n=await Jf(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=S0(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await I(e,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),h0(e,o),w0(e,o))}}catch{}}async function _0(e,t){try{let n=await Jf(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=T0(o);n.keys.has(s)||(n.keys.add(s),await I(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var zf,rl,I0=C(()=>{le();zo();e0();g0();rs();qf();Jr();ic();Vf();zf=new Map,rl=new Map});function lM(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
144
|
-
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;t.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return t}async function uM(e,t){let n=`${e.id}:${t.name}`;if(!Yf.has(n)){let r=await B(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);Yf.set(n,o)}return Yf.get(n)}async function R0(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await $r(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await uM(e,t),o=lM(await rt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await I(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Gn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await I(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Yf,$0=C(()=>{le();Cr();At();ma();Yf=new Map});import ol from"node:fs";import Zf from"node:path";import dM from"node:os";async function mM({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(pM,{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:O0})});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<O0)break}return r}function hM(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 M0(){try{return JSON.parse(ol.readFileSync(sl(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function gM(e){try{ol.mkdirSync(Zf.dirname(sl()),{recursive:!0});let t=sl()+".tmp";ol.writeFileSync(t,JSON.stringify(e)),ol.renameSync(t,sl())}catch{}}async function bM({fetchImpl:e,now:t=Date.now()}={}){if(or.rows&&t-or.at<wM)return or.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=M0(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await mM({sinceMs:o,cookie:n,fetchImpl:e});or={at:t,rows:s.map(hM).filter(Boolean),error:null}}catch(s){or={at:t,rows:null,error:String(s?.message||s)}}return or.rows}async function kM(e,t){let n=`${e.id}:${t.name}`;if(!Xf.has(n)){let r=await B(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);Xf.set(n,o)}return Xf.get(n)}async function N0(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!C0())return;let r=await bM({fetchImpl:n});if(!r||!r.length)return;let o=await nb(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await kM(e,t),a=M0(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=Gn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await I(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}gM({sinceMs:c,seen:[...i].slice(-yM)})}catch{}}var fM,C0,pM,O0,wM,or,sl,yM,Qf,Xf,ep=C(()=>{le();Gd();ma();fM=process.env.BOSTRAT_STATE_DIR||Zf.join(process.env.HOME||dM.tmpdir(),".bostrat"),C0=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,pM="https://cursor.com/api/dashboard/get-filtered-usage-events",O0=100;wM=5*6e4,or={at:0,rows:null,error:null},sl=()=>Zf.join(fM,"cursor-usage.json");yM=5e3;Qf=()=>({enabled:C0(),lastPollAt:or.at||null,error:or.error}),Xf=new Map});import Na from"node:fs/promises";import at from"node:path";import xM from"node:os";import vM from"node:net";import{randomUUID as SM}from"node:crypto";import{execFile as TM}from"node:child_process";import{promisify as AM}from"node:util";function CM(e){for(let t of ip(e))if(!F0.test(t))return t;return null}function U0(e){let t=ip(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function MM(e,t){let n=U0(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return at.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return at.resolve(e,n[r+1])}return e}function PM(e){let t=!1,n=!1;for(let r of ip(e)){if(!t){if(F0.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&tp(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,tp(r))return`serves the filesystem root ("${r}")`;continue}if(tp(r))return`serves the filesystem root ("${r}")`;if(NM(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function DM(e,t){if(at.resolve(e)===at.resolve(t))return!0;let[n,r]=await Promise.all([al(e),al(t)]);return n===r}async function jM(e){let n=(await Na.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 H0(at.join(e,o.name,op))?`./${o.name}`:null))).filter(Boolean)}async function LM(e,t){let[n,r]=await Promise.all([al(e),al(t)]),o=at.relative(n,r);if(o&&(o.startsWith("..")||at.isAbsolute(o)))throw Ca(`serve directory escapes the Track workspace: ${t}`)}async function FM(e,t){let n=PM(t);if(n)throw Ca(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await BM(e))throw Ca(`serve directory does not exist: ${e}`);let r=CM(t);if(!r)return;if(!r.includes("/")&&!await Fw(r,{loginShell:!0,failOpen:!0}))throw Ca(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(RM.test(t)||$M.has(r)||!IM.has(r)||!U0(t).includes("run")||OM.test(t))return;let o=MM(e,t);if(await H0(at.join(o,op)))return;let s=await jM(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Ca(`no ${op} in ${o}${a}`)}async function wn(){return Ht||(np||=(async()=>{try{Ht=JSON.parse(await Na.readFile(L0,"utf8"))}catch{Ht={views:[],adoptWatermarks:{}}}finally{np=null}return Ht.views||=[],Ht.adoptWatermarks||={},Ht})(),np)}async function qt(){await nt(L0,Ht)}function hn(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function lp(e){return Ma.get(e)||null}function q0(e,{name:t,box:n}={}){let r=Ma.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 Ma.set(e,o),o}function sp(e,t){let n=Ma.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function il(e,{error:t=null}={}){let n=Ma.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function K0(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 ap(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function UM(e,t){let n=ap(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!D0.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!D0.has(s))return s}}function Pa(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=vM.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 up(e){if(process.platform!=="darwin"){await Wt("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Wt("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 G0(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function HM(e,t){let n=new Set(e.views.map(o=>o.port)),r=G0();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=P0;o<=B0;o++)if(!(n.has(o)||r.has(o))&&!await Pa(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${P0}-${B0}`)}async function WM(e){try{let{stdout:t}=await Wt(j,W("list-sessions","-F","#{session_name}"));return t.split(`
|
|
145
|
-
`).some(n=>n.trim()===
|
|
143
|
+
`+r.slice(-n)),r}function zt(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
144
|
+
`).find(n=>n.trim())||"gh failed"}var Qo,BC,rl,Wf,ax,LC,Zo,ox,UC,HC,Ma,Na,qC,KC,GC,Uf,ns=O(()=>{Un();Qe();wn();Ca();nl();ue();Qo=PC(NC),BC={timeout:3e4,maxBuffer:8*1024*1024};rl=null;Wf="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",ax=Wf.replace("statusCheckRollup,","");LC=9e4,Zo=new Map;ox=(e,t)=>e.number-t.number;UC=3e4,HC=1500,Ma=new Map;Na=new Set(["squash","merge","rebase"]);qC=2500,KC=9e4,GC=e=>new Promise(t=>setTimeout(t,e));Uf=new Map});import{execFile as ZC}from"node:child_process";import{promisify as QC}from"node:util";async function tM(e,t){try{let{stdout:n}=await eM("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function nM(e,t,n,{rebase:r=!1,listPrs:o=Vt}={}){let s=await Aa(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await Ea(e.dir,n).catch(()=>0),i=t?await tM(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(h=>h.headRefName===t)||null,u=l?"open":"none"}catch{}let f=s||a>0,d=[];s&&d.push("commit"),r&&d.push("rebase"),(s||c)&&d.push("push"),f&&(u!=="open"||s||c)&&d.push("pr");let p=d.filter(m=>!(m==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:sl.filter(m=>d.includes(m)),defaults:sl.filter(m=>p.includes(m))}}async function wx(e,{branch:t,rebase:n=!1,listPrs:r=Vt}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(d=>d?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await Ho(s[0].dir).catch(()=>!1))return null;let a=[];for(let d of s)a.push(await nM(d,t,d.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(d=>d.dirty||d.aheadBase>0||d.openPr),c=i.length?i:a,l=sl.filter(d=>c.some(p=>p.available.includes(d))),u=sl.filter(d=>c.some(p=>p.defaults.includes(d))),f=c.filter(d=>d.openPr);return{available:l,defaults:u,prState:f.length?"open":c.every(d=>d.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(d=>({name:d.name,slug:d.slug,dirty:d.dirty,unpushed:d.unpushed,prState:d.prState,openPr:d.openPr}))}}var eM,sl,gx=O(()=>{jc();ns();eM=QC(ZC),sl=["commit","rebase","push","pr"]});function al(e,t,n){let{state:r,reason:o}=Td(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of Da)a-c.at>=yx()&&Da.delete(i);Da.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=Da.get(e);return s?Date.now()-s.at>=yx()?(Da.delete(e),!1):s.busy:!1}async function zf(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Ee(e,{lines:Le}).catch(()=>"");if(!al(e,t,r))return Kg(t,r)?{working:!0,prompted:!1}:n;if(Vr(t,r)){let o=await fn(e).catch(()=>r),s=or({name:e,agent:t},o);if(s)return!Dc({name:e,agent:t},s,o)||Fb(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function eo(e,t){return(await zf(e,t)).working}var yx,Da,Ba=O(()=>{tt();Tc();wf();yf();yx=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Da=new Map});import{execFile as rM}from"node:child_process";import{promisify as oM}from"node:util";async function rs(e){if(!e)return null;let t=bx.get(e);if(t&&Date.now()-t.at<aM)return t.branch;let n=null;try{let{stdout:r}=await sM("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return bx.set(e,{at:Date.now(),branch:n}),n}function kx(e){return String(e||"").match(iM)?.[1]??null}function cM(e,t){let n=kx(e);return!!n&&!!t&&n===t}function xx(e,t){let n=kx(e);return!!n&&n!==t}function lM(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 os(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of lM(e))n(await rs(r));return t}async function il(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=>cM(t,a.id));if(s)return s;for(let a of r)if(!(a.archivedAt||a.transferredTo)&&(await os(a)).includes(t))return a;return null}var sM,aM,bx,iM,ss=O(()=>{sM=oM(rM),aM=5e3,bx=new Map;iM=/^track\/.+-([0-9a-f]{8})$/});import{execFile as uM}from"node:child_process";import{promisify as dM}from"node:util";async function Jf(e,t,{midTurn:n=eo,pollMs:r=fM,capMs:o=pM}={}){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 _x(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){Sx.lastIndex=n;let o=Sx.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(`
|
|
145
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+kM(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 Yf(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function Rx(e){let t=_x(e),n=t.match(gM);if(!n)return null;let r=Yf(t,n.index);return bM.test(r)?null:{repo:r.match(Ix)?.[1]??null,head:r.match(xM)?.[1]??null}}function $x(e){let t=_x(e),n=t.match(yM);if(!n)return null;let r=Yf(t,n.index),o=Yf(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("-")){vM.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Ix)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function AM(e,t,{exclude:n=()=>!1}={}){let r=async i=>{yn("observer");let{stdout:c}=await vx("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",TM],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw lr(l)&&ur(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-mM&&!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 EM(e,t){if(t)return[t];let n=[];for(let r of Ox(e)){let o=await me(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await cn().catch(()=>[])){let o=await me(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Ox(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 _M(e,t,n){let r=await os(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||SM(c.headRefName,e.id),i=[];for(let c of await EM(e,t?.repo??null)){let l;try{l=await AM(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await qe(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===e.id&&f.announced)return{settled:!0};if(f&&f.trackId!==e.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==e.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function Cx(e,t,{delays:n=Tx,turnEnd:r}={}){let o=Rx(t.command);if(!o)return null;let s=!!$x(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(dr.has(a))return null;dr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await _M(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await mt({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===e.id&&await hn(f,d.number))?null:(await Jf(t.box,t.agent,r),await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{dr.delete(a)}})();return c.catch(()=>dr.delete(a)),c}function Mx(e,t,{delays:n=Tx,turnEnd:r,openedWait:o}={}){let s=$x(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(dr.has(a))return null;dr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let m=Ox(e);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let g=await me(h).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=(await os(e).catch(()=>[])).filter(m=>m!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,h)=>{let g=await qe(c,m.number);return g&&g.trackId!==e.id?null:(g||await mt({repo:c,number:m.number,trackId:e.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Ra(c,m.number),h&&((await L(e).catch(()=>[])).some(A=>A.type==="pr.state.changed"&&(A.payload?.change==="auto-armed"||A.payload?.change==="merged")&&A.payload?.number===m.number)||(await Jf(t.box,t.agent,r),await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(N=>setTimeout(N,m));let h=[];for(let N of f)try{yn("observer");let{stdout:C}=await vx("gh",["pr","view",String(l),"--repo",N,"--json",IM],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await qt({slug:N})}}),T=JSON.parse(C||"null");T?.number&&h.push({slug:N,pr:T})}catch(C){lr(C)&&ur(C)}let g=h.find(N=>N.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!g)continue;c=g.slug;let x=g.pr;if(!x?.number)continue;if(x.state==="CLOSED")return null;if(x.state!=="MERGED"){if(p=x,s.auto)return d(x,!0);continue}let k=await qe(c,x.number);if(k&&k.trackId!==e.id)return null;k||await mt({repo:c,number:x.number,trackId:e.id,url:x.url,headRef:x.headRefName,title:x.title,source:"merge-observed"}),await Jf(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 R=N=>N.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="opened"&&C.payload?.number===x.number),M=0;if(!R(A)){if(Rx(t.command)&&await hn(c,x.number)){let N=Date.now();M=N+1,await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:N,change:"opened",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c,selfMerge:!0}})}else if((await qe(c,x.number))?.announced){let N=o?.capMs??hM,C=o?.pollMs??wM,T=Date.now()+N;for(;Date.now()<T&&(await new Promise(j=>setTimeout(j,C)),!R(await L(e).catch(()=>[]))););}}return await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),M),change:"merged",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c}}),await xn(c,x.number).catch(()=>{}),await kn(c,x.number).catch(()=>{}),await ir(c,x.number,"merged"),x}return p?d(p,!1):null}finally{dr.delete(a)}})();return i.catch(()=>dr.delete(a)),i}var vx,Tx,fM,pM,mM,hM,wM,Ax,Ex,gM,yM,bM,Sx,kM,Ix,xM,SM,vM,TM,dr,IM,Nx=O(()=>{ue();Un();Qe();Ca();wn();fr();Ba();ss();vx=dM(uM),Tx=[4e3,15e3,45e3],fM=2500,pM=5*6e4;mM=15e3,hM=9e4,wM=2e3,Ax=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,Ex=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,gM=new RegExp(`${Ax}${Ex}create\\b`),yM=new RegExp(`${Ax}${Ex}merge\\b`),bM=/\s--web\b/,Sx=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,kM=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");Ix=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,xM=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,SM=xx;vM=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);TM="number,url,title,headRefName,createdAt";dr=new Set;IM="number,url,title,headRefName,state"});import RM from"node:fs/promises";import Zf from"node:path";import $M from"node:os";function Xf(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function cl(e){let t=OM.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Xf(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Px(s);return{socket:s,pid:a,name:Xf(n,"from-name"),mode:Xf(n,"from-mode"),text:r.trim()}}function Px(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function CM(){return process.env.CLAUDE_CONFIG_DIR?Zf.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Zf.join($M.homedir(),".claude","sessions")}async function MM(e){let t=Px(e);if(!t)return null;try{let n=await RM.readFile(Zf.join(CM(),`${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 Dx(e,t){let n=await MM(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 OM,Qf=O(()=>{OM=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function ep(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function tp({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!ep(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),Bx);try{let a=await jx(`${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 Lx(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(!ep(s))return{ok:!1,source:"unconfigured"};let i=ll.get(e.id)||0;if(a()-i<NM)return{ok:!1,source:"debounced"};ll.set(e.id,a());let c=await ul({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return ll.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),f=await tp({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||ll.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function ul({env:e=process.env}={}){if(!ep(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),Bx);try{let r=await jx(`${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 Bx,jx,NM,ll,np=O(()=>{Qe();Bx=1e4,jx=(...e)=>fetch(...e);NM=10*6e4,ll=new Map});import{randomUUID as PM}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 Fx(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function Ux(e){return`job|${e.box}|id:${e.id}`}async function op(e){return rp.has(e.id)?rp.get(e.id):(dl.has(e.id)||dl.set(e.id,L(e).then(t=>{let n={keys:new Set,cmdN:0};for(let r of t)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(ja({role:o,sourceId:r.payload.sourceId})),n.keys.add(ja({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(ja({role:"user",sourceId:r.payload.sourceId})),n.keys.add(ja({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Fx({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(Ux({id:r.payload.id,box:r.boxId}));return rp.set(e.id,n),n}).finally(()=>dl.delete(e.id))),dl.get(e.id))}async function Hx(e,t){let n=new Set;try{let r=(await op(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"?cl(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 Dx(a,La),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=_g(o);if(i){await _(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),Lx(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=Ak(o.text);if(c){let l=Ik(c),u;if(e.workspace?.kind==="multi"){let m=await as(e).catch(()=>[]);u=m.length?m.map(h=>({name:h.name,slug:h.repo??null})):void 0}let f=(e.workspace?.kind==="multi"?null:await rs(e.cwd).catch(()=>null))||e.workspace.branch,d=await wx(e,{branch:f,rebase:l}).catch(()=>null),p=d?null:_k(c);if(d&&d.available.length===0)continue;await _(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:PM(),ts:o.ts,excerpt:c,branch:f,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:d?.available,defaults:d?.defaults,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||u,offered:d?.defaults||p?.offered||void 0,toPr:p?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function Wx(e,t){try{let n=await op(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=Fx(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}}),Cx(e,o),Mx(e,o))}}catch{}}async function qx(e,t){try{let n=await op(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Ux(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 rp,dl,Kx=O(()=>{ue();Yo();gx();Nx();ss();Qf();fr();gc();np();rp=new Map,dl=new Map});function DM(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
146
|
+
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;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 BM(e,t){let n=`${e.id}:${t.name}`;if(!sp.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);sp.set(n,o)}return sp.get(n)}async function Gx(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Pr(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await BM(e,t),o=DM(await it(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await _(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:tr(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await _(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var sp,Vx=O(()=>{ue();Br();Mt();xa();sp=new Map});import fl from"node:fs";import ip from"node:path";import jM from"node:os";async function UM({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(FM,{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:zx})});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<zx)break}return r}function HM(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 Yx(){try{return JSON.parse(fl.readFileSync(pl(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function qM(e){try{fl.mkdirSync(ip.dirname(pl()),{recursive:!0});let t=pl()+".tmp";fl.writeFileSync(t,JSON.stringify(e)),fl.renameSync(t,pl())}catch{}}async function GM({fetchImpl:e,now:t=Date.now()}={}){if(pr.rows&&t-pr.at<WM)return pr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Yx(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await UM({sinceMs:o,cookie:n,fetchImpl:e});pr={at:t,rows:s.map(HM).filter(Boolean),error:null}}catch(s){pr={at:t,rows:null,error:String(s?.message||s)}}return pr.rows}async function VM(e,t){let n=`${e.id}:${t.name}`;if(!ap.has(n)){let r=await L(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);ap.set(n,o)}return ap.get(n)}async function Xx(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!Jx())return;let r=await GM({fetchImpl:n});if(!r||!r.length)return;let o=await yb(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await VM(e,t),a=Yx(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=tr(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await _(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}qM({sinceMs:c,seen:[...i].slice(-KM)})}catch{}}var LM,Jx,FM,zx,WM,pr,pl,KM,cp,ap,lp=O(()=>{ue();tf();xa();LM=process.env.BOSTRAT_STATE_DIR||ip.join(process.env.HOME||jM.tmpdir(),".bostrat"),Jx=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,FM="https://cursor.com/api/dashboard/get-filtered-usage-events",zx=100;WM=5*6e4,pr={at:0,rows:null,error:null},pl=()=>ip.join(LM,"cursor-usage.json");KM=5e3;cp=()=>({enabled:Jx(),lastPollAt:pr.at||null,error:pr.error}),ap=new Map});import Ha from"node:fs/promises";import lt from"node:path";import zM from"node:os";import JM from"node:net";import{randomUUID as YM}from"node:crypto";import{execFile as XM}from"node:child_process";import{promisify as ZM}from"node:util";function sN(e){for(let t of wp(e))if(!r0.test(t))return t;return null}function o0(e){let t=wp(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function aN(e,t){let n=o0(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return lt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return lt.resolve(e,n[r+1])}return e}function cN(e){let t=!1,n=!1;for(let r of wp(e)){if(!t){if(r0.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&up(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,up(r))return`serves the filesystem root ("${r}")`;continue}if(up(r))return`serves the filesystem root ("${r}")`;if(iN(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function uN(e,t){if(lt.resolve(e)===lt.resolve(t))return!0;let[n,r]=await Promise.all([ml(e),ml(t)]);return n===r}async function dN(e){let n=(await Ha.readdir(e,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await s0(lt.join(e,o.name,pp))?`./${o.name}`:null))).filter(Boolean)}async function fN(e,t){let[n,r]=await Promise.all([ml(e),ml(t)]),o=lt.relative(n,r);if(o&&(o.startsWith("..")||lt.isAbsolute(o)))throw Fa(`serve directory escapes the Track workspace: ${t}`)}async function pN(e,t){let n=cN(t);if(n)throw Fa(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await lN(e))throw Fa(`serve directory does not exist: ${e}`);let r=sN(t);if(!r)return;if(!r.includes("/")&&!await tg(r,{loginShell:!0,failOpen:!0}))throw Fa(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(nN.test(t)||rN.has(r)||!tN.has(r)||!o0(t).includes("run")||oN.test(t))return;let o=aN(e,t);if(await s0(lt.join(o,pp)))return;let s=await dN(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Fa(`no ${pp} in ${o}${a}`)}async function Tn(){return Yt||(dp||=(async()=>{try{Yt=JSON.parse(await Ha.readFile(n0,"utf8"))}catch{Yt={views:[],adoptWatermarks:{}}}finally{dp=null}return Yt.views||=[],Yt.adoptWatermarks||={},Yt})(),dp)}async function Zt(){await at(n0,Yt)}function vn(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function yp(e){return Ua.get(e)||null}function i0(e,{name:t,box:n}={}){let r=Ua.get(e);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:e,name:t||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Ua.set(e,o),o}function mp(e,t){let n=Ua.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function hl(e,{error:t=null}={}){let n=Ua.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function c0(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 hp(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function mN(e,t){let n=hp(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!e0.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!e0.has(s))return s}}function Wa(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=JM.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 bp(e){if(process.platform!=="darwin"){await Xt("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Xt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function l0(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function hN(e,t){let n=new Set(e.views.map(o=>o.port)),r=l0();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=Zx;o<=Qx;o++)if(!(n.has(o)||r.has(o))&&!await Wa(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Zx}-${Qx}`)}async function wN(e){try{let{stdout:t}=await Xt(U,K("list-sessions","-F","#{session_name}"));return t.split(`
|
|
147
|
+
`).some(n=>n.trim()===is(e))}catch{return!1}}async function qa(e){try{await Xt(U,K("kill-session","-t",is(e)))}catch{}}async function kp(e){if(!await ra())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=is(e.slug),n=e.serveCwd??e.cwd,r=lt.dirname(process.execPath);await Xt(U,K("new-session","-d","-s",t,"-c",n)),await Xt(U,K("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Xt(U,K("send-keys","-t",t,"Enter")),await Xt(U,K("send-keys","-t",t,"-l",e.cmd)),await Xt(U,K("send-keys","-t",t,"Enter"))}async function t0(e,t=12){try{let{stdout:n}=await Xt(U,K("capture-pane","-t",is(e),"-p"));return String(n||"").trimEnd().split(`
|
|
146
148
|
`).filter(Boolean).slice(-t).join(`
|
|
147
|
-
`)}catch{return""}}async function KM(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await WM(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await j0(e.slug)||"server exited before listening"),mn.delete(e.id),await hn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Pa(e.port))return mn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await hn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>qM?(e.state="error",e.error=await j0(e.slug)||"never started listening",await hn(e,"error",{error:e.error}),!0):!1;let o=(mn.get(e.id)||0)+1;return mn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function cl(){if(!rp){rp=!0;try{let e=await wn(),t=!1;for(let n of e.views)await KM(n)&&(t=!0);t&&await qt()}finally{rp=!1}}}async function ll({trackId:e}={}){await cl();let t=await wn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function V0(e){let n=(await wn()).views.find(r=>r.id===e);return n?{...n}:null}function fp(e){return Ht&&Ht.views.find(t=>t.slug===e)||null}async function pp({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=at.resolve(r,o??"."),f=i==="detected"?null:async()=>{await LM(r,u),await FM(u,n)},d=await wn();if(i!=="detected"){let w=ap(t),y=String(n).trim(),b=d.views.find(v=>v.trackId===e&&(v.slug===w||ap(v.name)===w||v.cmd===y&&at.resolve(v.serveCwd??v.cwd)===u));if(b)return GM(d,b,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=sr)throw new Error(`view cap reached (${sr} live) \u2014 stop one first`);let p={id:SM().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:UM(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await HM(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await dp(p),d.views.push(p),await qt(),await hn(p,"created",{origin:i}),sp(e,p.id),i==="detected"&&await Pa(p.port)&&(p.state="live",p.lastHealthyAt=Date.now(),await qt(),await hn(p,"live")),{...p}}async function GM(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 DM(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Pa(t.port))return await qt(),sp(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=sr)throw new Error(`view cap reached (${sr} live) \u2014 stop one first`);return t.origin==="detected"?(await up(t.port),t.origin="agent"):await Ba(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),mn.delete(t.id),await dp(t),await qt(),await hn(t,"created",{restarted:!0,reused:!0}),sp(t.trackId,t.id),{...t,reused:!0}}async function z0(e){let n=(await wn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await up(n.port):await Ba(n.slug),n.state!=="stopped"&&(n.state="stopped",mn.delete(n.id),await qt(),await hn(n,"stopped")),{...n}}async function J0(e){let t=await wn(),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>=sr)throw new Error(`view cap reached (${sr} live) \u2014 stop one first`);return await Ba(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),mn.delete(n.id),await dp(n),await qt(),await hn(n,"created",{restarted:!0}),{...n}}async function Y0(e){let t=await wn(),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 up(r.port):await Ba(r.slug),t.views.splice(n,1),mn.delete(r.id),await qt(),await hn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function X0(e){let t=await wn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Ba(r.slug),mn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await qt()),n.length}async function Z0(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await wn(),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 qt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(VM)){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(!G0().has(c)&&!n.views.some(l=>l.port===c)&&await Pa(c))try{i.push(await pp({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 Wt,EM,L0,P0,B0,sr,_M,ss,D0,Ca,IM,op,RM,$M,OM,ip,F0,tp,NM,BM,H0,al,Ht,np,Ma,cp,W0,mn,qM,rp,VM,ul=C(()=>{Xe();le();Nn();Wt=AM(TM),EM=process.env.BOSTRAT_STATE_DIR||at.join(process.env.HOME||xM.tmpdir(),".bostrat"),L0=at.join(EM,"views.json"),[P0,B0]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),sr=Number(process.env.BOSTRAT_VIEW_MAX)||3,_M="bostrat-view-",ss=e=>`${_M}${e}`,D0=new Set(["bostrat","www","api","views","app"]),Ca=e=>Object.assign(new Error(e),{status:400}),IM=new Set(["npm","pnpm","yarn","bun"]),op="package.json",RM=/[|;&`]|\$\(/,$M=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),OM=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,ip=e=>String(e).trim().split(/\s+/).filter(Boolean),F0=/^[A-Za-z_][A-Za-z0-9_]*=/;tp=e=>e==="/",NM=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");BM=e=>Na.stat(e).then(t=>t.isDirectory(),()=>!1),H0=e=>Na.stat(e).then(t=>t.isFile(),()=>!1),al=e=>Na.realpath(e).catch(()=>at.resolve(e));Ht=null,np=null;Ma=new Map,cp=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,W0=12e3;mn=new Map,qM=12e4;rp=!1;VM=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function dl(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>JM?null:n}function YM(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Q0(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await B(e)}catch{return}let r=dl(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await zr(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(mp.get(e.id)||0)<zM)return;mp.set(e.id,s);let a;try{a=await Lt(e.cwd,{caller:"reconcile"})}catch{return}let i=await ns(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await de(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await ut({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await cn(l,c.number))){if(!YM(u,r))return;let d=await B(e).catch(()=>null);if(d&&dl(d)==null)return;await I(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await I(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),mp.delete(e.id)}var zM,mp,JM,hp=C(()=>{le();es();Tr();Qn();Ra();rs();zM=6e3,mp=new Map,JM=30*6e4});import wp from"node:os";import{execFile as XM}from"node:child_process";import{promisify as ZM}from"node:util";async function fl(){if(process.platform!=="darwin")return wp.freemem();try{let{stdout:e}=await QM("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:wp.freemem()}catch{return wp.freemem()}}var QM,gp=C(()=>{QM=ZM(XM)});import Oe from"node:fs/promises";import Me from"node:path";import nx from"node:os";import{randomUUID as eN}from"node:crypto";import{execFile as tN}from"node:child_process";import{promisify as nN}from"node:util";import{chromium as rN}from"playwright-core";async function Vt(){return Yr||(yp||=(async()=>{try{Yr=JSON.parse(await Oe.readFile(ox,"utf8"))}catch{Yr={mocks:[]}}finally{yp=null}return Yr.mocks||=[],Yr})(),yp)}async function yt(){await nt(ox,Yr)}function ix(e,t){Gt(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Gt(e,t.v,"png")}function Qe(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function bt(e,t){let n=ja(e.cwd);return{html:Me.join(n,`${e.id}.v${t}.html`),png:Me.join(n,`${e.id}.v${t}.png`)}}function Da(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?Me.join(e.cwd,r):null}function Rt(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 xp(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(fN)}function pN(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 mN(e){let t=Buffer.alloc(24),n=await Oe.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function hN({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 wN(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((p,w)=>{r.onload=p,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,m=-1;for(let p=0;p<s;p++)for(let w=p*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>d&&(d=y),p<f&&(f=p),m=p);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:m}}},t.toString("base64"))}async function gN(e,t,{bg:n,crop:r}){let o=await e.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((f,d)=>{c.onload=f,c.onerror=()=>d(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function yN({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await rN.launch({executablePath:sN,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new It("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:as})).newPage();a.setDefaultTimeout(is),await a.goto(`file://${e}`,{waitUntil:"load",timeout:is}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:is});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await wN(a,i),u=l&&hN({...l,pad:cN*as,align:as});u&&(i=await gN(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<sx)throw new It(`capture produced a ${i.length}-byte image`);let c=xp(i);if(!c)throw new It("output is not a PNG");return await Oe.writeFile(t,i),{width:Math.round(c.width/as),height:Math.round(c.height/as),dpr:as,renderMs:Date.now()-r}}catch(s){throw s instanceof It?s:new It(/timeout/i.test(s?.message||"")?`render timed out after ${is}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function tx({htmlPath:e,outPath:t,width:n}){let r=await Oe.mkdtemp(Me.join(nx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await kp(aN,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:is,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new It(c?.killed?`render timed out after ${is}ms`:"firefox exited non-zero",s)}let a=await Oe.stat(t).catch(()=>null);if(!a||a.size<sx)throw new It(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=xp(await mN(t));if(!i)throw new It("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Oe.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function bN(e){if(oN==="firefox")return tx(e);try{return await yN(e)}catch(t){if(/failed to launch/i.test(t?.message||""))return tx(e);throw t}}function cx(e){return bp=bp.then(e,e),bp}async function lx(e,t){let n=await Vt(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await yt(),await Qe(r,"rendering",{version:t});try{let o=await fl();if(o<lN)throw new It(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=bt(r,t),a=await kN({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Oe.rm(s.png,{force:!0}).catch(()=>{});return}vp(r,{v:t,source:"html",htmlRel:Me.relative(r.cwd,s.html),pngRel:Me.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await yt(),await Qe(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Kt(r),Gt(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await yt(),await Qe(r,"error",{version:t,error:r.error}),Kt(r)}}}function vp(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 xN({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?iN:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function La({trackId:e,group:t}={}){let n=await Vt(),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 gn(e){let n=(await Vt()).mocks.find(r=>r.id===e);return n?{...n}:null}function ux({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=pN(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>ex)throw new Error(`video must be ${ex/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function dx({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=xN({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),m=ux({html:n,image:r,video:o}),p=await Vt(),w={id:eN().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Oe.mkdir(ja(s),{recursive:!0}),p.mocks.push(w),d?(await Oe.writeFile(bt(w,1).html,n),await yt(),await Qe(w,"created"),Kt(w),Gt(w,1,"html"),cx(()=>lx(w.id,1))):o?(await px(w,1,o,m),await yt(),await Qe(w,"created"),await Qe(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),Kt(w),ix(w,w.versions[0])):(await mx(w,1,r),await yt(),await Qe(w,"created"),await Qe(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),Kt(w),Gt(w,1,"png")),{...w}}async function fx(e,{html:t,image:n,video:r,width:o}={}){let a=(await Vt()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=ux({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 Oe.mkdir(ja(a.cwd),{recursive:!0}),i?(a.state="queued",await Oe.writeFile(bt(a,l).html,t),await yt(),await Qe(a,"updated",{version:l}),Kt(a),Gt(a,l,"html"),cx(()=>lx(a.id,l))):r?(await px(a,l,r,c),await yt(),await Qe(a,"updated",{version:l}),await Qe(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Rt(a)?.durationS??null}),Kt(a),ix(a,a.versions.find(u=>u.v===l))):(await mx(a,l,n),await yt(),await Qe(a,"updated",{version:l}),await Qe(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Kt(a),Gt(a,l,"png")),{...a}}async function vN(e){try{let{stdout:t}=await kp(uN,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:ax,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(t),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function SN(e,t){try{await kp(dN,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:ax,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Oe.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function px(e,t,n,r){let o=ja(e.cwd),s=Me.join(o,`${e.id}.v${t}.${r.ext}`),a=Me.join(o,`${e.id}.v${t}.poster.png`);await Oe.writeFile(s,n);let i=await vN(s),c=await SN(s,a);vp(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Me.relative(e.cwd,s),posterRel:c?Me.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function mx(e,t,n){let r=xp(n);if(!r)throw new Error("image must be a PNG");let o=bt(e,t);await Oe.writeFile(o.png,n),vp(e,{v:t,source:"image",htmlRel:null,pngRel:Me.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function hx(e){let t=await Vt(),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 Qe(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function wx(e){let t=await Vt(),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=bt(r,o);await Oe.rm(s.html,{force:!0}).catch(()=>{}),await Oe.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Oe.rm(Me.join(r.cwd,s),{force:!0}).catch(()=>{});return await yt(),await Qe(r,"deleted"),St("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function gx(e){let n=(await Vt()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Me.join(rx,"mock-archive",e.id),o=ja(e.cwd),s=Me.join(r,".bostrat","mocks");await Oe.mkdir(s,{recursive:!0});let a=await Oe.readdir(o).catch(()=>[]);for(let i of a)await Oe.copyFile(Me.join(o,i),Me.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 Oe.stat(Me.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(),Kt(i)}return await yt(),n.length}async function yx(e){let t=await Vt(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await yt();for(let r of n)St("mocks",r.id).catch(()=>{})}return n.length}async function bx(){if(!ve())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Pe("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Vt();for(let r of n.mocks){await Kt(r);for(let o of r.versions||[])o.htmlRel&&await Gt(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Gt(r,o.v,"png"),o.videoRel&&await Gt(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var kp,rx,ox,oN,sN,aN,iN,as,cN,sx,is,lN,ex,uN,dN,ax,It,Yr,yp,Kt,Gt,ja,fN,kN,bp,cs=C(()=>{le();Nn();gp();Bn();kp=nN(tN),rx=process.env.BOSTRAT_STATE_DIR||Me.join(process.env.HOME||nx.tmpdir(),".bostrat"),ox=Me.join(rx,"mocks.json"),oN=process.env.BOSTRAT_MOCK_ENGINE||"chromium",sN=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",aN=process.env.BOSTRAT_FIREFOX_BIN||"firefox",iN=1300,as=2,cN=32,sx=1500,is=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,lN=700*1024*1024,ex=100*1024*1024,uN=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",dN=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",ax=2e4,It=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
149
|
+
`)}catch{return""}}async function yN(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await wN(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await t0(e.slug)||"server exited before listening"),Sn.delete(e.id),await vn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Wa(e.port))return Sn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await vn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>gN?(e.state="error",e.error=await t0(e.slug)||"never started listening",await vn(e,"error",{error:e.error}),!0):!1;let o=(Sn.get(e.id)||0)+1;return Sn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function wl(){if(!fp){fp=!0;try{let e=await Tn(),t=!1;for(let n of e.views)await yN(n)&&(t=!0);t&&await Zt()}finally{fp=!1}}}async function gl({trackId:e}={}){await wl();let t=await Tn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function u0(e){let n=(await Tn()).views.find(r=>r.id===e);return n?{...n}:null}function xp(e){return Yt&&Yt.views.find(t=>t.slug===e)||null}async function Sp({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=lt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await fN(r,u),await pN(u,n)},d=await Tn();if(i!=="detected"){let h=hp(t),g=String(n).trim(),x=d.views.find(k=>k.trackId===e&&(k.slug===h||hp(k.name)===h||k.cmd===g&<.resolve(k.serveCwd??k.cwd)===u));if(x)return bN(d,x,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(h=>h.state==="live"||h.state==="starting").length>=mr)throw new Error(`view cap reached (${mr} live) \u2014 stop one first`);let m={id:YM().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:mN(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await hN(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await kp(m),d.views.push(m),await Zt(),await vn(m,"created",{origin:i}),mp(e,m.id),i==="detected"&&await Wa(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Zt(),await vn(m,"live")),{...m}}async function bN(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 uN(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Wa(t.port))return await Zt(),mp(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=mr)throw new Error(`view cap reached (${mr} live) \u2014 stop one first`);return t.origin==="detected"?(await bp(t.port),t.origin="agent"):await qa(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Sn.delete(t.id),await kp(t),await Zt(),await vn(t,"created",{restarted:!0,reused:!0}),mp(t.trackId,t.id),{...t,reused:!0}}async function d0(e){let n=(await Tn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await bp(n.port):await qa(n.slug),n.state!=="stopped"&&(n.state="stopped",Sn.delete(n.id),await Zt(),await vn(n,"stopped")),{...n}}async function f0(e){let t=await Tn(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=mr)throw new Error(`view cap reached (${mr} live) \u2014 stop one first`);return await qa(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Sn.delete(n.id),await kp(n),await Zt(),await vn(n,"created",{restarted:!0}),{...n}}async function p0(e){let t=await Tn(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await bp(r.port):await qa(r.slug),t.views.splice(n,1),Sn.delete(r.id),await Zt(),await vn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function m0(e){let t=await Tn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await qa(r.slug),Sn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Zt()),n.length}async function h0(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Tn(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await Zt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(kN)){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(!l0().has(c)&&!n.views.some(l=>l.port===c)&&await Wa(c))try{i.push(await Sp({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var Xt,QM,n0,Zx,Qx,mr,eN,is,e0,Fa,tN,pp,nN,rN,oN,wp,r0,up,iN,lN,s0,ml,Yt,dp,Ua,gp,a0,Sn,gN,fp,kN,yl=O(()=>{tt();ue();Wn();Xt=ZM(XM),QM=process.env.BOSTRAT_STATE_DIR||lt.join(process.env.HOME||zM.tmpdir(),".bostrat"),n0=lt.join(QM,"views.json"),[Zx,Qx]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),mr=Number(process.env.BOSTRAT_VIEW_MAX)||3,eN="bostrat-view-",is=e=>`${eN}${e}`,e0=new Set(["bostrat","www","api","views","app"]),Fa=e=>Object.assign(new Error(e),{status:400}),tN=new Set(["npm","pnpm","yarn","bun"]),pp="package.json",nN=/[|;&`]|\$\(/,rN=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),oN=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,wp=e=>String(e).trim().split(/\s+/).filter(Boolean),r0=/^[A-Za-z_][A-Za-z0-9_]*=/;up=e=>e==="/",iN=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");lN=e=>Ha.stat(e).then(t=>t.isDirectory(),()=>!1),s0=e=>Ha.stat(e).then(t=>t.isFile(),()=>!1),ml=e=>Ha.realpath(e).catch(()=>lt.resolve(e));Yt=null,dp=null;Ua=new Map,gp=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,a0=12e3;Sn=new Map,gN=12e4;fp=!1;kN=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function bl(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>SN?null:n}function vN(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function w0(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await L(e)}catch{return}let r=bl(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await eo(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(vp.get(e.id)||0)<xN)return;vp.set(e.id,s);let a;try{a=await Vt(e.cwd,{caller:"reconcile"})}catch{return}let i=await os(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await me(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await mt({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await hn(l,c.number))){if(!vN(u,r))return;let d=await L(e).catch(()=>null);if(d&&bl(d)==null)return;await _(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await _(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),vp.delete(e.id)}var xN,vp,SN,Tp=O(()=>{ue();ns();Un();wn();Ba();ss();xN=6e3,vp=new Map,SN=30*6e4});import Ap from"node:os";import{execFile as TN}from"node:child_process";import{promisify as AN}from"node:util";async function kl(){if(process.platform!=="darwin")return Ap.freemem();try{let{stdout:e}=await EN("vm_stat",[],{timeout:3e3}),t=Number(/page size of (\d+) bytes/.exec(e)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(e)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*t;return r>0?r:Ap.freemem()}catch{return Ap.freemem()}}var EN,Ep=O(()=>{EN=AN(TN)});import Me from"node:fs/promises";import Pe from"node:path";import k0 from"node:os";import{randomUUID as _N}from"node:crypto";import{execFile as IN}from"node:child_process";import{promisify as RN}from"node:util";import{chromium as $N}from"playwright-core";async function tn(){return to||(_p||=(async()=>{try{to=JSON.parse(await Me.readFile(S0,"utf8"))}catch{to={mocks:[]}}finally{_p=null}return to.mocks||=[],to})(),_p)}async function vt(){await at(S0,to)}function E0(e,t){en(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&en(e,t.v,"png")}function nt(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function Tt(e,t){let n=Ga(e.cwd);return{html:Pe.join(n,`${e.id}.v${t}.html`),png:Pe.join(n,`${e.id}.v${t}.png`)}}function Ka(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Pe.join(e.cwd,r):null}function Dt(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 $p(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function xl(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(BN)}function jN(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 LN(e){let t=Buffer.alloc(24),n=await Me.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function FN({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 UN(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((m,h)=>{r.onload=m,r.onerror=()=>h(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let h=m*o*4,g=0;g<o;g++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(g<u&&(u=g),g>d&&(d=g),m<f&&(f=m),p=m);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},t.toString("base64"))}async function HN(e,t,{bg:n,crop:r}){let o=await e.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((f,d)=>{c.onload=f,c.onerror=()=>d(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function WN({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await $N.launch({executablePath:v0,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new ht("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:cs})).newPage();a.setDefaultTimeout(ls),await a.goto(`file://${e}`,{waitUntil:"load",timeout:ls}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:ls});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await UN(a,i),u=l&&FN({...l,pad:MN*cs,align:cs});u&&(i=await HN(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<T0)throw new ht(`capture produced a ${i.length}-byte image`);let c=$p(i);if(!c)throw new ht("output is not a PNG");return await Me.writeFile(t,i),{width:Math.round(c.width/cs),height:Math.round(c.height/cs),dpr:cs,renderMs:Date.now()-r}}catch(s){throw s instanceof ht?s:new ht(/timeout/i.test(s?.message||"")?`render timed out after ${ls}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function b0({htmlPath:e,outPath:t,width:n}){let r=await Me.mkdtemp(Pe.join(k0.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Rp(g0,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:ls,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new ht(`firefox not found (${g0})`,""):new ht(c?.killed?`render timed out after ${ls}ms`:"firefox exited non-zero",s)}let a=await Me.stat(t).catch(()=>null);if(!a||a.size<T0)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=$p(await LN(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 Me.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function qN(e){if(ON==="firefox")return b0(e);try{return await WN(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await b0(e)}catch(n){throw/not found/.test(n?.message||"")?new ht(`no browser installed \u2014 chromium missing at ${v0} 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 _0(e){return Ip=Ip.then(e,e),Ip}async function I0(e,t){let n=await tn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await vt(),await nt(r,"rendering",{version:t});try{let o=await kl();if(o<NN)throw new ht(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Tt(r,t),a=await KN({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Me.rm(s.png,{force:!0}).catch(()=>{});return}Op(r,{v:t,source:"html",htmlRel:Pe.relative(r.cwd,s.html),pngRel:Pe.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await vt(),await nt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Qt(r),en(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await vt(),await nt(r,"error",{version:t,error:r.error}),Qt(r)}}}function Op(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 GN({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?CN:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Va({trackId:e,group:t}={}){let n=await tn(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function An(e){let n=(await tn()).mocks.find(r=>r.id===e);return n?{...n}:null}function R0({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&&!xl(t))throw new Error("image must be a PNG");if(n){let o=jN(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>y0)throw new Error(`video must be ${y0/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function $0({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=GN({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=R0({html:n,image:r,video:o}),m=await tn(),h={id:_N().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Me.mkdir(Ga(s),{recursive:!0}),m.mocks.push(h),d?(await Me.writeFile(Tt(h,1).html,n),await vt(),await nt(h,"created"),Qt(h),en(h,1,"html"),_0(()=>I0(h.id,1))):o?(await C0(h,1,o,p),await vt(),await nt(h,"created"),await nt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),Qt(h),E0(h,h.versions[0])):(await M0(h,1,r),await vt(),await nt(h,"created"),await nt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),Qt(h),en(h,1,"png")),{...h}}async function O0(e,{html:t,image:n,video:r,width:o}={}){let a=(await tn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=R0({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await Me.mkdir(Ga(a.cwd),{recursive:!0}),i?(a.state="queued",await Me.writeFile(Tt(a,l).html,t),await vt(),await nt(a,"updated",{version:l}),Qt(a),en(a,l,"html"),_0(()=>I0(a.id,l))):r?(await C0(a,l,r,c),await vt(),await nt(a,"updated",{version:l}),await nt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Dt(a)?.durationS??null}),Qt(a),E0(a,a.versions.find(u=>u.v===l))):(await M0(a,l,n),await vt(),await nt(a,"updated",{version:l}),await nt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Qt(a),en(a,l,"png")),{...a}}async function VN(e){try{let{stdout:t}=await Rp(PN,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:A0,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 zN(e,t){try{await Rp(DN,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:A0,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Me.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function C0(e,t,n,r){let o=Ga(e.cwd),s=Pe.join(o,`${e.id}.v${t}.${r.ext}`),a=Pe.join(o,`${e.id}.v${t}.poster.png`);await Me.writeFile(s,n);let i=await VN(s),c=await zN(s,a);Op(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Pe.relative(e.cwd,s),posterRel:c?Pe.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function M0(e,t,n){let r=$p(n);if(!r)throw new Error("image must be a PNG");let o=Tt(e,t);await Me.writeFile(o.png,n),Op(e,{v:t,source:"image",htmlRel:null,pngRel:Pe.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function N0(e){let t=await tn(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await nt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function P0(e){let t=await tn(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Tt(r,o);await Me.rm(s.html,{force:!0}).catch(()=>{}),await Me.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Me.rm(Pe.join(r.cwd,s),{force:!0}).catch(()=>{});return await vt(),await nt(r,"deleted"),Rt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function D0(e){let n=(await tn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Pe.join(x0,"mock-archive",e.id),o=Ga(e.cwd),s=Pe.join(r,".bostrat","mocks");await Me.mkdir(s,{recursive:!0});let a=await Me.readdir(o).catch(()=>[]);for(let i of a)await Me.copyFile(Pe.join(o,i),Pe.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Me.stat(Pe.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Qt(i)}return await vt(),n.length}async function B0(e){let t=await tn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await vt();for(let r of n)Rt("mocks",r.id).catch(()=>{})}return n.length}async function j0(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tn();for(let r of n.mocks){await Qt(r);for(let o of r.versions||[])o.htmlRel&&await en(r,o.v,"html"),(o.pngRel||o.posterRel)&&await en(r,o.v,"png"),o.videoRel&&await en(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Rp,x0,S0,ON,v0,g0,CN,cs,MN,T0,ls,NN,y0,PN,DN,A0,ht,to,_p,Qt,en,Ga,BN,KN,Ip,us=O(()=>{ue();Wn();Ep();Kn();Rp=RN(IN),x0=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||k0.tmpdir(),".bostrat"),S0=Pe.join(x0,"mocks.json"),ON=process.env.BOSTRAT_MOCK_ENGINE||"chromium",v0=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",g0=process.env.BOSTRAT_FIREFOX_BIN||"firefox",CN=1300,cs=2,MN=32,T0=1500,ls=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,NN=700*1024*1024,y0=100*1024*1024,PN=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",DN=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",A0=2e4,ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
148
150
|
${n.trim().split(`
|
|
149
151
|
`).slice(-6).join(`
|
|
150
|
-
`)}`:t),this.name="MockRenderError"}},Yr=null,yp=null;Kt=e=>Ye("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Gt=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Da(e,r,"video"):r&&Da(e,r,n)||(n==="html"?bt(e,t).html:null);return o?Pn("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};ja=e=>Me.join(e,".bostrat","mocks");fN=Buffer.from([137,80,78,71,13,10,26,10]);kN=bN,bp=Promise.resolve()});import TN from"node:fs/promises";import Tx from"node:path";import AN from"node:os";import{randomUUID as Ax}from"node:crypto";async function bn(){return yn||(Sp||=(async()=>{let e=!0;try{yn=JSON.parse(await TN.readFile(Ex,"utf8"))}catch{yn={flows:[]},e=!1}finally{Sp=null}if(yn.flows||=[],!e&&ve()){let{records:t,source:n}=await Pe("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&yn.flows.push(r.data);yn.flows.length&&await ls().catch(()=>{})}}return yn})(),Sp)}async function ls(){await nt(Ex,yn)}function Tp(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function _x({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>kx)throw new Error(`steps must be an array of 1-${kx}`);let r=await La({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>vx)throw new Error(`${l}: caption over ${vx} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Sx)throw new Error(`${l}: title over ${Sx} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let m=r.filter(v=>v.id===d);if(m.length||(m=r.filter(v=>v.name.toLowerCase()===d.toLowerCase())),!m.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(m.length>1)throw new Error(`${l}: "${d}" is ambiguous (${m.map(v=>`mock:${v.id}`).join(", ")}) \u2014 use the id`);let p=m[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(p.versions||[]).some(v=>v.v===w)))throw new Error(`${l}: "${p.name}" has no captured v${c.v} to pin`);let y=o.findIndex(v=>v.mockId===p.id),b=y>=0?o.splice(y,1)[0].id:Ax().slice(0,8);s.push({id:b,mockId:p.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function _N(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>xx)throw new Error(`name over ${xx} chars`);return e.trim()}async function Ix({trackId:e}={}){let t=await bn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function IN(e){let n=(await bn()).flows.find(r=>r.id===e);return n?{...n}:null}async function Rx(e){let t=await IN(e);if(!t)return null;let n=new Map((await La({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=Rt(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 $x({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=_N(t),i=await bn(),c=i.flows.find(m=>m.trackId===e&&m.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await _x({trackId:e,steps:n}),f=Date.now(),d={id:Ax().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await ls(),await Tp(d,"created"),ml(d),{...d}}async function Ox(e,{steps:t,note:n}={}){let o=(await bn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await _x({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 ls(),await Tp(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),ml(o),{...o}}async function Cx(e){let t=await bn(),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 ls(),await Tp(r,"deleted"),St("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Mx(e){return(await bn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function Nx(e){let n=(await bn()).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(),ml(r);return n.length&&await ls(),n.length}async function Px(e){let t=await bn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await ls();for(let r of n)St("flows",r.id).catch(()=>{})}return n.length}async function Bx(){if(!ve())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Pe("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await bn();for(let r of n.flows)await ml(r);return{pushed:n.flows.length,source:"plane"}}var EN,Ex,kx,xx,vx,Sx,yn,Sp,ml,Ap=C(()=>{le();Nn();cs();Bn();EN=process.env.BOSTRAT_STATE_DIR||Tx.join(process.env.HOME||AN.tmpdir(),".bostrat"),Ex=Tx.join(EN,"flows.json"),kx=20,xx=80,vx=500,Sx=80,yn=null,Sp=null;ml=e=>Ye("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import vn from"node:fs/promises";import Sn from"node:path";import RN from"node:os";import{randomUUID as $N}from"node:crypto";function Fa(e){return ON.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function zt(){return kn||(Ep||=(async()=>{let e=!0;try{kn=JSON.parse(await vn.readFile(jx,"utf8"))}catch{kn={runbooks:[]},e=!1}finally{Ep=null}return kn.runbooks||=[],!e&&ve()&&await CN().catch(()=>{}),kn})(),Ep)}async function Zr(){await nt(jx,kn)}function wl(e){return Ua(Ye("runbooks",{...e},{trackId:e.trackId}))}async function CN(){let{records:e,source:t}=await Pe("runbooks");if(!(t!=="plane"||!e.length)){await vn.mkdir(hl,{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 Js("runbooks",r.id,o.v,"md");typeof s=="string"&&await vn.writeFile(xn(r,o.v),s).catch(()=>{})}kn.runbooks.push(r)}kn.runbooks.length&&await Zr()}}function us(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function xn(e,t){return Sn.join(hl,`${e.id}.v${t}.md`)}function Lx(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 Fx(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>Dx)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${Dx/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Fa(e);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return t}function MN(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function NN({trackId:e,status:t}={}){let n=await zt(),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 Ux({trackId:e,status:t}={}){let n=await zt(),r=await NN({trackId:e,status:t});if(!ve())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Pe("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 _p(e){let t=await PN(e);if(t)return t;if(!ve())return null;let{records:n,source:r}=await Pe("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 Hx(e,t){let n=await Js("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function PN(e){let n=(await zt()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function Wx({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=Fx(r),c=await zt(),l={id:$N().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 vn.mkdir(hl,{recursive:!0}),await vn.writeFile(xn(l,1),r),l.versions.push({v:1,mdRel:Sn.relative(Xr,xn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Zr(),await us(l,"created",{version:1}),wl(l),Ua(Pn("runbooks",l.id,1,"md",xn(l,1))),{...l}}async function qx(e,{md:t,note:n}={}){let o=(await zt()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");MN(o,"update");let s=Fx(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await vn.mkdir(hl,{recursive:!0}),await vn.writeFile(xn(o,a),t),o.versions.push({v:a,mdRel:Sn.relative(Xr,xn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Zr(),await us(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),wl(o),Ua(Pn("runbooks",o.id,a,"md",xn(o,a))),{...o}}async function Kx(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await zt()).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 Zr(),await us(r,t==="done"?"done":"reopened"),wl(r),{...r})}async function Gx(e){let t=await zt(),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 vn.rm(Sn.join(Xr,o.mdRel),{force:!0}).catch(()=>{});return await Zr(),r.trackDeletedAt||await us(r,"deleted"),Ua(St("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function Vx(e){let n=(await zt()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await us(r,"orphaned"),r.cwd=null,wl(r);return n.length&&await Zr(),n.length}async function zx(e){let t=await zt(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await us(r,"deleted");for(let o of r.versions||[])await vn.rm(Sn.join(Xr,o.mdRel),{force:!0}).catch(()=>{});Ua(St("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Zr()),n.length}async function Jx(){if(!ve())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Pe("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await zt();for(let r of n.runbooks){await Ye("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Pn("runbooks",r.id,o.v,"md",Sn.join(Xr,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Xr,jx,hl,Dx,ON,kn,Ep,Ua,gl=C(()=>{Nn();le();Bn();Xr=process.env.BOSTRAT_STATE_DIR||Sn.join(process.env.HOME||RN.tmpdir(),".bostrat"),jx=Sn.join(Xr,"runbooks.json"),hl=Sn.join(Xr,"runbooks"),Dx=256*1024,ON=[{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/}];kn=null,Ep=null;Ua=e=>Promise.resolve(e).catch(()=>{})});import Ip from"node:fs/promises";import Ha from"node:path";import{randomUUID as BN}from"node:crypto";function $p(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return DN.has(t)?"image":jN.has(t)?"doc":null}function LN(e){let r=Ha.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 Zx(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>Yx)throw new Error(`file too large \u2014 max ${Math.floor(Yx/(1024*1024))} MB`);let r=LN(t),o=Ha.extname(r).slice(1),s=$p(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=BN().slice(0,8),i=Rp(e);await Ip.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Ip.writeFile(Ha.join(i,c),n,{mode:384}),await Ip.chmod(Ha.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${Xx}/${c}`,kind:s,size:n.length}}function Qx(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Op(e){return e.role==="user"&&FN.test((e.text||"").trim())}var Xx,Rp,Yx,DN,jN,FN,Cp=C(()=>{Xx=".bostrat/uploads",Rp=e=>Ha.join(e,Xx),Yx=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,DN=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),jN=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"]);FN=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Qr(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var ev=C(()=>{});import{execFile as UN}from"node:child_process";import{promisify as HN}from"node:util";function Mp(e){return`refs/bostrat/wip/${e}`}function KN(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function rv(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function GN(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:qN}async function Np(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await ar(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await ar(r,["remote","get-url","origin"])}catch{return tv.has(r)||(tv.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 Uo(r,"bostrat: wip snapshot"),{stdout:s}=await ar(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(yl.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=Mp(n);return await ar(r,["push","--force","origin",`${o}:${i}`],nv),yl.set(r,a),await I({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
151
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
152
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
152
|
+
`)}`:t),this.name="MockRenderError"}},to=null,_p=null;Qt=e=>et("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),en=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Ka(e,r,"video"):r&&Ka(e,r,n)||(n==="html"?Tt(e,t).html:null);return o?qn("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ga=e=>Pe.join(e,".bostrat","mocks");BN=Buffer.from([137,80,78,71,13,10,26,10]);KN=qN,Ip=Promise.resolve()});import JN from"node:fs/promises";import W0 from"node:path";import YN from"node:os";import{randomUUID as q0}from"node:crypto";async function _n(){return En||(Cp||=(async()=>{let e=!0;try{En=JSON.parse(await JN.readFile(K0,"utf8"))}catch{En={flows:[]},e=!1}finally{Cp=null}if(En.flows||=[],!e&&Ae()){let{records:t,source:n}=await je("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&En.flows.push(r.data);En.flows.length&&await ds().catch(()=>{})}}return En})(),Cp)}async function ds(){await at(K0,En)}function Mp(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 G0({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>L0)throw new Error(`steps must be an array of 1-${L0}`);let r=await Va({trackId:e}),o=[...n],s=[];for(let i=0;i<t.length;i++){let c=t[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>U0)throw new Error(`${l}: caption over ${U0} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>H0)throw new Error(`${l}: title over ${H0} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(k=>k.id===d);if(p.length||(p=r.filter(k=>k.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(k=>`mock:${k.id}`).join(", ")}) \u2014 use the id`);let m=p[0],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(m.versions||[]).some(k=>k.v===h)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let g=o.findIndex(k=>k.mockId===m.id),x=g>=0?o.splice(g,1)[0].id:q0().slice(0,8);s.push({id:x,mockId:m.id,caption:u,title:f,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function ZN(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>F0)throw new Error(`name over ${F0} chars`);return e.trim()}async function V0({trackId:e}={}){let t=await _n();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function QN(e){let n=(await _n()).flows.find(r=>r.id===e);return n?{...n}:null}async function z0(e){let t=await QN(e);if(!t)return null;let n=new Map((await Va({trackId:t.trackId})).map(r=>[r.id,r]));return t.steps=t.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=Dt(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 J0({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=ZN(t),i=await _n(),c=i.flows.find(p=>p.trackId===e&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await G0({trackId:e,steps:n}),f=Date.now(),d={id:q0().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await ds(),await Mp(d,"created"),Sl(d),{...d}}async function Y0(e,{steps:t,note:n}={}){let o=(await _n()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await G0({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await ds(),await Mp(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Sl(o),{...o}}async function X0(e){let t=await _n(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await ds(),await Mp(r,"deleted"),Rt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Z0(e){return(await _n()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function Q0(e){let n=(await _n()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),Sl(r);return n.length&&await ds(),n.length}async function eS(e){let t=await _n(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await ds();for(let r of n)Rt("flows",r.id).catch(()=>{})}return n.length}async function tS(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await _n();for(let r of n.flows)await Sl(r);return{pushed:n.flows.length,source:"plane"}}var XN,K0,L0,F0,U0,H0,En,Cp,Sl,Np=O(()=>{ue();Wn();us();Kn();XN=process.env.BOSTRAT_STATE_DIR||W0.join(process.env.HOME||YN.tmpdir(),".bostrat"),K0=W0.join(XN,"flows.json"),L0=20,F0=80,U0=500,H0=80,En=null,Cp=null;Sl=e=>et("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import $n from"node:fs/promises";import On from"node:path";import eP from"node:os";import{randomUUID as tP}from"node:crypto";function za(e){return nP.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function nn(){return In||(Pp||=(async()=>{let e=!0;try{In=JSON.parse(await $n.readFile(rS,"utf8"))}catch{In={runbooks:[]},e=!1}finally{Pp=null}return In.runbooks||=[],!e&&Ae()&&await rP().catch(()=>{}),In})(),Pp)}async function ro(){await at(rS,In)}function Tl(e){return Ja(et("runbooks",{...e},{trackId:e.trackId}))}async function rP(){let{records:e,source:t}=await je("runbooks");if(!(t!=="plane"||!e.length)){await $n.mkdir(vl,{recursive:!0});for(let n of e){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await Zs("runbooks",r.id,o.v,"md");typeof s=="string"&&await $n.writeFile(Rn(r,o.v),s).catch(()=>{})}In.runbooks.push(r)}In.runbooks.length&&await ro()}}function fs(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Rn(e,t){return On.join(vl,`${e.id}.v${t}.md`)}function oS(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 sS(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>nS)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${nS/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=za(e);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return t}function oP(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function sP({trackId:e,status:t}={}){let n=await nn(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function aS({trackId:e,status:t}={}){let n=await nn(),r=await sP({trackId:e,status:t});if(!Ae())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await je("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Dp(e){let t=await aP(e);if(t)return t;if(!Ae())return null;let{records:n,source:r}=await je("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function iS(e,t){let n=await Zs("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function aP(e){let n=(await nn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function cS({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=sS(r),c=await nn(),l={id:tP().slice(0,8),trackId:e,trackName:t||e,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await $n.mkdir(vl,{recursive:!0}),await $n.writeFile(Rn(l,1),r),l.versions.push({v:1,mdRel:On.relative(no,Rn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await ro(),await fs(l,"created",{version:1}),Tl(l),Ja(qn("runbooks",l.id,1,"md",Rn(l,1))),{...l}}async function lS(e,{md:t,note:n}={}){let o=(await nn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");oP(o,"update");let s=sS(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await $n.mkdir(vl,{recursive:!0}),await $n.writeFile(Rn(o,a),t),o.versions.push({v:a,mdRel:On.relative(no,Rn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await ro(),await fs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Tl(o),Ja(qn("runbooks",o.id,a,"md",Rn(o,a))),{...o}}async function uS(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await nn()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await ro(),await fs(r,t==="done"?"done":"reopened"),Tl(r),{...r})}async function dS(e){let t=await nn(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await $n.rm(On.join(no,o.mdRel),{force:!0}).catch(()=>{});return await ro(),r.trackDeletedAt||await fs(r,"deleted"),Ja(Rt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function fS(e){let n=(await nn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await fs(r,"orphaned"),r.cwd=null,Tl(r);return n.length&&await ro(),n.length}async function pS(e){let t=await nn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await fs(r,"deleted");for(let o of r.versions||[])await $n.rm(On.join(no,o.mdRel),{force:!0}).catch(()=>{});Ja(Rt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await ro()),n.length}async function mS(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await nn();for(let r of n.runbooks){await et("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await qn("runbooks",r.id,o.v,"md",On.join(no,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var no,rS,vl,nS,nP,In,Pp,Ja,Al=O(()=>{Wn();ue();Kn();no=process.env.BOSTRAT_STATE_DIR||On.join(process.env.HOME||eP.tmpdir(),".bostrat"),rS=On.join(no,"runbooks.json"),vl=On.join(no,"runbooks"),nS=256*1024,nP=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];In=null,Pp=null;Ja=e=>Promise.resolve(e).catch(()=>{})});import Bp from"node:fs/promises";import Ya from"node:path";import{randomUUID as iP}from"node:crypto";function Lp(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return cP.has(t)?"image":lP.has(t)?"doc":null}function uP(e){let r=Ya.basename(String(e||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function gS(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>hS)throw new Error(`file too large \u2014 max ${Math.floor(hS/(1024*1024))} MB`);let r=uP(t),o=Ya.extname(r).slice(1),s=Lp(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=iP().slice(0,8),i=jp(e);await Bp.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Bp.writeFile(Ya.join(i,c),n,{mode:384}),await Bp.chmod(Ya.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${wS}/${c}`,kind:s,size:n.length}}function yS(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Fp(e){return e.role==="user"&&dP.test((e.text||"").trim())}var wS,jp,hS,cP,lP,dP,Up=O(()=>{wS=".bostrat/uploads",jp=e=>Ya.join(e,wS),hS=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,cP=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),lP=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"]);dP=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function oo(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var bS=O(()=>{});import{execFile as fP}from"node:child_process";import{promisify as pP}from"node:util";function Hp(e){return`refs/bostrat/wip/${e}`}function wP(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function SS(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function gP(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:hP}async function Wp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await hr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await hr(r,["remote","get-url","origin"])}catch{return kS.has(r)||(kS.add(r),t.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await Wo(r,"bostrat: wip snapshot"),{stdout:s}=await hr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(El.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=Hp(n);return await hr(r,["push","--force","origin",`${o}:${i}`],xS),El.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(`
|
|
153
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function qp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await hr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await hr(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await Wo(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await hr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(El.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=wP(n);return await hr(r,["push","origin",`${o}:${i}`],xS),El.set(r,a),await _({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,rescue:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: rescued stranded work ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: rescue push failed: ${String(o?.message||o).split(`
|
|
154
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function vS({listTracks:e,sweepMs:t=gP(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||i?.transferredTo||await Wp(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 mP,hP,xS,hr,El,kS,Kp=O(()=>{jc();ue();mP=pP(fP),hP=12e4,xS=6e4;hr=(e,t,n=15e3)=>mP("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),El=new Map,kS=new Set});import yP from"node:fs";import Gp from"node:fs/promises";import bP from"node:path";function vP(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function TS(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(!vP(t))return{restored:!1,reason:"plane not configured"};let s=So(e),a=bP.join(s,`${o}.ndjson`);try{if(yP.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 d=new AbortController,p=setTimeout(()=>d.abort(),SP),m;try{m=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${kP}`,{headers:c,signal:d.signal})}finally{clearTimeout(p)}if(m.status===404)return{restored:!1,reason:"unknown to plane"};if(!m.ok)return{restored:!1,reason:`plane ${m.status}`};let h=await m.json();for(let g of h.events||[])l.push(JSON.stringify(g));if(l.length>xP)return{restored:!1,reason:"event count over safety cap"};if(h.next==null)break;u=h.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await Gp.mkdir(s,{recursive:!0});let f=`${a}.pull-${process.pid}-${Date.now()}`;return await Gp.writeFile(f,l.join(`
|
|
153
155
|
`)+`
|
|
154
|
-
`),await
|
|
156
|
+
`),await Gp.rename(f,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var kP,xP,SP,AS=O(()=>{Qe();ue();kP=1e3,xP=5e5,SP=3e4});var ov={};Ru(ov,{HANDOFF_LATEST_REL:()=>so,KNOWN_AGENTS:()=>ii,TRACK_NAME_MAX:()=>am,addTrackRepo:()=>xm,adoptTrack:()=>jm,agentAvailability:()=>$l,announceNodeUpdate:()=>on,answerFullscreenOffer:()=>Om,answerPrompt:()=>Cl,archiveTrack:()=>io,archiveTrackForMergedPr:()=>kn,augmentFirstPromptAfterHandoff:()=>PS,buildAgentCmd:()=>MS,canResumeSubtask:()=>tv,cancelQueued:()=>ui,cancelQueuedToDraft:()=>mm,cancelStopJob:()=>di,cancelSubtask:()=>Hl,changedRepos:()=>as,commitTransferOut:()=>Fl,createSetupTrack:()=>Tm,createTrack:()=>Pl,deleteTrack:()=>Em,demoteTransferredTracks:()=>Bm,ensureLeadEpochs:()=>jl,forceMergeSubtask:()=>Hm,getHandoffJob:()=>xs,getSubtaskJob:()=>AD,getTrack:()=>ne,getTransferOutJob:()=>Nm,healDeadLead:()=>si,interruptLead:()=>Nl,isArchiving:()=>hD,isHandoffNoteEcho:()=>nm,isInterruptEcho:()=>rm,isModelSwitchEcho:()=>om,leadCliDead:()=>_m,listSubtaskJobs:()=>co,listTracks:()=>ae,matchTranscriptsToBoxes:()=>jS,mergeNotifyMessage:()=>XS,navigatePrompt:()=>pm,notifyTrackForMergedPr:()=>xn,notifyTrackForResolvedPr:()=>Dl,pendingOffers:()=>dm,queueShipCompleteNotify:()=>wm,queuedMessages:()=>ao,reapArchivedOrphans:()=>$m,reconcileHandoffOutcome:()=>ZS,reconcileInterruptedHandoffs:()=>Mm,reconcileInterruptedSubtasks:()=>Lm,reconcileSubtaskOutcome:()=>ev,refreshStandingInstructions:()=>vm,refuseIfTransferred:()=>Dm,rehydrateLead:()=>pi,removeTrackRepo:()=>Sm,renameTrack:()=>Rm,reposOf:()=>gt,resolveTrackBox:()=>La,resolveTrackBoxByHookToken:()=>um,restartLead:()=>Bl,resumeAgentCmd:()=>lm,sayToLead:()=>_t,sendOptionsForAgent:()=>Gt,setTrackDraft:()=>bm,setTrackModel:()=>km,startLeadHandoff:()=>Ll,startSpinUpView:()=>gm,startSubtask:()=>Um,startTransferOut:()=>Pm,stopJobActive:()=>US,subtaskMessages:()=>fm,trackMessages:()=>Ol,unarchiveTrack:()=>Im});import H from"node:fs/promises";import G from"node:path";import{randomUUID as ks}from"node:crypto";import{execFile as TP}from"node:child_process";import{promisify as AP}from"node:util";function MS(e,{skipPermissions:t=!0}={}){let n=Rl[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 lm(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Rl[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&&!Ct(r,t)){let c=o&&_e(o)?` --name ${o}`:"";return`${s.base} --session-id ${t}${c}${i}`}return`${s.base} --resume ${t}${i}`}case"cursor":return`${s.base} --continue${i}`;case"codex":return`${s.base} resume --last${i}`;default:return`${s.base}${i}`}}function ci(e){let t=Rl[e];if(t)return process.env[t.envVar]||t.base}async function $l({maxAgeMs:e=6e4}={}){if(wr.value&&Date.now()-wr.at<e)return wr.value;let t=wr.value,n=await Promise.all(ii.map(async r=>{let o=await dc(ci(r),{retries:[]});return o===Io?[r,t?.[r]??!0]:[r,o===uc]}));return wr.value=Object.fromEntries(n),wr.at=Date.now(),wr.value}async function li(e,t=!0){let n=MS(e,{skipPermissions:t});if(await dc(n)===_o){wr.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.":rd[r]?`Install it on this node: ${rd[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 PS(e,t=so){return`${e.trim()}
|
|
155
157
|
|
|
156
|
-
(Read ${t} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function
|
|
157
|
-
`;return Lp=Lp.then(()=>D.mkdir(Jt,{recursive:!0})).then(()=>D.appendFile(tP,n)).catch(()=>{}),Lp}async function ur({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let f=null;try{f=JSON.parse(await D.readFile(Fp,"utf8"))}catch{}if(f?.latest===t&&f?.current===e)return{announced:0,deduped:!0}}let l=await K(),u=0;for(let f of l.tracks)f.archivedAt||!f.leadBox||(fe(f,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await Z().catch(()=>{}),s==="available")try{await D.mkdir(L.dirname(Fp),{recursive:!0}),await D.writeFile(Fp,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}function gv(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=>iP.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 yv(e,t,n,{kind:r}={}){let o=await ae(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Qa(o.agent);await Hn({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Fe(t,n,{...jt(o.agent),kind:r||"orchestration"}),await Y({flow:"orchestration",phase:"sent",trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await K(),i=re(a,e);if(!i)return;let c=i.boxes.find(l=>l.name===t);fe(i,"orchestration-prompt",{box:t,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await Z()}async function cP(e){let t=L.join(Co(e),`${e.id}.ndjson`),n;try{n=await D.stat(t)}catch{return null}let r=iv.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return iv.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function lP(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Lf(n.name,n.agent)}async function Yt(e,t=null){let n=t??new Map((await Tt()).map(i=>[i.name,i])),r=e.boxes.map(i=>{let c=n.get(i.name),l=cr.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([cP(e),lP(e,r)]);return{...e,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Ya.has(e.id)}}function bv(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),f=l.createdAt-uP,d=r[u+1]?.createdAt??1/0,m=o.find(p=>!s.has(p.id)&&p.birthMs>=f&&p.birthMs<d);m&&(s.add(m.id),c.push({box:l.name,sessionId:m.id}))}return c}async function kv(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(d=>d.box.sessionId),a=Zw(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=bv(u,a,d=>i.has(d));for(let d of f){let m=u.find(p=>p.name===d.box);m&&(m.sessionId=d.sessionId),await Ww(d.box,d.sessionId),n=!0}}return n}async function oe(){let e=await K();await kv(e)&&await Z();let t=new Map((await Tt()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Yt(n,t)))}async function Oa(e,t){let n=await K();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 Qp(e){let t=Ps(e);if(!t)return null;let n=await K();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Hh(o.hookTokenHash,t))return{track:r,box:o};return null}async function te(e){let t=await K(),n=re(t,e);return n?(await kv(t)&&await Z(),Yt(n)):null}async function em(e){let t=await B(e).catch(()=>[]),n;try{n=Wn(t)}catch{return[]}return ay(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function zp(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(dP[e.agent]||[]).some(n=>n.test(t))}function Jp(e){let t=(e.text||"").trim();return t?fP.some(n=>n.test(t)):!1}async function Sl(e,t={}){let n=await te(e);if(!n)return null;let r=new Set([...n.events.filter(S=>S.type==="orchestration-prompt"&&S.prompt).map(S=>String(S.prompt).trim()),...n.events.filter(S=>S.type==="said"&&S.text).map(S=>String(S.text).trim())]),o=bs(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=lp(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...K0(n),...l],d={windows:[...Iy(n),...Ry(n,Sc),...a,...u]},p=(await Promise.all(n.boxes.map(S=>Zd(S,t)))).flat(),w=p.map(S=>{let z={kind:"message",...S};if(z.role==="assistant"&&(z.text||"").trim()&&z.tools?.length){let Sr=wa(z.agent).cliTools,fo=z.tools.filter(J=>!Sr.has(J));return{...z,tools:fo.length?fo:void 0}}return z}).map(S=>{if(S.role!=="user")return S;let z=kc(S.text);return z?z.kind==="suppress"?null:{...S,note:z,text:Xg(z)}:S}).filter(Boolean).filter(S=>!(S.role==="user"&&r.has((S.text||"").trim()))).filter(S=>!zp(S)).filter(S=>!Jp(S)).filter(S=>!Vp(S)).filter(S=>!Op(S)).filter(S=>S.role!=="assistant"||!!(S.text||"").trim()||!!S.images?.length).filter(S=>!$y(S,d)),y=w.filter(S=>S.failed&&S.text===Or);if(y.length){let S=new Map;for(let z of y){if(!S.has(z.box)){let J=[...(await Se(z.box,{lines:Be}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(U=>U[1]);S.set(z.box,J.length?J[J.length-1]:null)}let Sr=S.get(z.box);Sr&&(z.text=`\u25A0 ${Sr}`)}}let b=await Promise.all(n.boxes.map(S=>ib(S,t)));await _0(n,b.flat());let{agentTurnBoxes:v}=await A0(n,w);for(let S of n.boxes)v.has(S.name)&&S.cwd&&Ki(S.cwd);await nk(n),await Z0(n,w).catch(()=>{});for(let S of n.boxes)S.agent==="codex"&&S.state==="live"&&await R0(n,S),S.agent==="cursor"&&S.state==="live"&&await N0(n,S);let R=await Promise.all(n.boxes.map(S=>ab(S,t)));await E0(n,R.flat().filter(S=>!Up(S.box,S.ts,u)));let A=await B(n),V=c&&i.viewId?i.viewId:null,O=mk(A),M=Wn(A).filter(S=>!(S.kind==="message"&&(zp(S)||Jp(S)||Vp(S)||Op(S)))&&!(S.kind==="view-card"&&V&&S.viewId===V)),T=hk(M,O),q=M.filter(S=>!(S.kind==="message"&&S.role==="assistant"&&!T.has(S)&&Up(S.box,S.ts,O))),G=Md(A,q).map(S=>({kind:"command-batch",id:S.id,ts:S.pending?S.ts:(S.anchorTs??S.ts)-1,anchorTs:S.anchorTs,anchorBox:S.anchorBox,agent:S.agent,boxRole:S.boxRole,commands:S.commands,pending:S.pending})).filter(S=>!Up(S.anchorBox,S.anchorTs??S.ts,u)),ue=oy(sy([...q,...G].filter(S=>S.ts!=null).sort((S,z)=>S.ts-z.ts))),Q=mP(n),N=Q.size?ue.filter(S=>S.kind==="event"||!Q.has(S.box??S.anchorBox)):ue,$=n.boxes.find(S=>S.name===n.leadBox)||null,F=$?await Qs($.cwd,$.sessionId,{fallbackNewest:!1}):null;if(!F&&$){let S=[...A].reverse().find(z=>z.type==="context.usage"&&z.boxId===$.name);S?.payload?.window&&(F={pct:S.payload.pct??(S.payload.occupancy!=null?S.payload.occupancy/S.payload.window:null),occupancy:S.payload.occupancy??null,window:S.payload.window,windowKnown:!0,model:S.payload.model||null})}let ce=[...p].reverse().find(S=>S.role==="assistant"&&S.model),Re=ce?.model||null,De=[...n.events||[]].reverse().find(S=>S.type==="model-change"),be=F?.model||Re;De&&(!ce?.ts||De.at>=ce.ts)&&(be=De.to);let g=$?await Se($.name,{lines:Be}).catch(()=>""):"",h=$&&Ur($.agent,g)?await rn($.name).catch(()=>g):g,k=$?Jn($,h):null;if(k&&$){let S=Ic($,k,h);if(S){let z=Sb($.name,k.promptId);z.action!=="give-up"&&(z.action==="send"&&(Ie($.name,S.keys,{kind:"auto-reject"}).catch(()=>{}),z.first&&I(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:$.name,provider:$.agent,payload:{ts:Date.now(),promptId:k.promptId,rule:S.id}}).catch(()=>{})),k=null)}}!k&&$&&!aa($.agent,g)&&!to(n.id).length&&(k=Od(N,{leadBoxId:$.name,provider:$.agent,isAnswered:S=>A.some(z=>z.type==="prompt.answered"&&z.payload?.promptId===S)})),k&&rk(n,$,k);let _=N.filter(S=>!(S.kind==="message"&&S.role==="assistant"&&S.ask&&!String(S.text||"").trim()&&!S.images?.length)),ee=!!$&&Zc($.name,$.agent,g)&&!k;$&&!$.readyAt&&(ee||k||g&&pc($.agent,g))&&($.readyAt=Date.now(),await Z());let Te=!!$&&!$.readyAt&&Date.now()-(n.createdAt||0)<pP;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&$&&pc($.agent,g)&&!k){let S=await K(),z=re(S,n.id);z?.setup&&!z.setup.connectedAt&&(z.setup.connectedAt=Date.now(),n.setup.connectedAt=z.setup.connectedAt,await I(z,{type:"provider.connected",actor:"system",source:"pane",boxId:$.name,provider:$.agent,payload:{provider:$.agent,ts:z.setup.connectedAt}}).catch(()=>{}),await Z())}let je=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Le=null;if(c){let S=Date.now()-i.startedAt;if(i.viewId&&!ee){let z=il(n.id);z&&(fe(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:z.finishedAt}),await Z())}else if(k||S>W0&&!ee&&!i.viewId||S>cp){let z=k?"needs-input":S>cp?"timeout":"no-view";il(n.id,{error:z})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:$?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:z}}).catch(()=>{})}else Le={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:_,context:F?{pct:F.pct,occupancy:F.occupancy,window:F.window,windowKnown:F.windowKnown,model:F.model}:null,models:{current:be,options:Jb($?.agent)},pendingPrompt:k,working:ee,stopping:Sv(n.id),leadStarting:Te,handoff:je,view:Le,queued:to(n.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function mP(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 te(e);if(!n)return null;let r=ro(e).find(f=>f.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await B(n),a=Wn(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Md(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await Se(o,{lines:Be}).catch(()=>"");u=Zc(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Tl(e,{promptId:t,answers:n,text:r}={}){let o=re(await K(),e);if(!o)throw new Error(`track "${e}" not found`);ni(e,"prompt-answer");let s=o.boxes.find(p=>p.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 sf(s);if(i||(i=await hP(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 p=typeof r=="string"?r.trim():"";if(!p)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(p)||p.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Fe(o.leadBox,p,{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 p=ty(i,n||[],typeof r=="string"?r:"");if(!p)throw new Error("no answer selected \u2014 pick an option or reply in chat");await xt(o.id,p,{kind:"prompt-answer"}),c={mode:"say",value:p,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let p=bb(i,n||[]);await Ie(o.leadBox,p,{kind:"answer-prompt"}),c={mode:"keys",value:p.join(" "),count:p.length}}else{let p=Eg(i,n||[]);await Fe(o.leadBox,p,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:p,count:1}}if(i.answerMode==="say"){let p=i.questions||[];for(let w=0;w<p.length;w++){let y=p[w],b=new Set(Array.isArray(n?.[w])?n[w]:[]),v=[...b].map(A=>y.options?.[A]).filter(Boolean).map(A=>({label:A.label,description:A.description||""})),R=(y.options||[]).map((A,V)=>b.has(V)?null:A?.label).filter(Boolean);Sf(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??p.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:v,rejected:R})}return await Y({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(p=>l.options?.[p]).filter(Boolean).map(p=>({label:p.label,description:p.description||""})),d=(l.options||[]).map((p,w)=>u.has(w)?null:p?.label).filter(Boolean),m=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:f,rejected:d,...m?{control:m}:{}},await Y({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Sf(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function hP(e,t){if(!t)return null;let n=await B(e).catch(()=>[]);return Od(Wn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function nm(e,{promptId:t,questionIndex:n}={}){let r=re(await K(),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 sf(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=kb(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 Al(e){let t=Ga.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ga.set(e,t)),t}function to(e){return(Ga.get(e)?.items??[]).map(t=>({...t}))}function ti(e){let t=Ga.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 rm(e){let t=ti(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=re(await K(),e);if(o)return r=hs(o,n),await Z(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function om(e){let t=Al(e);t.timer||(t.timer=setInterval(()=>gP(e).catch(()=>{}),wP))}async function gP(e){let t=Ga.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 K(),e);if(!n||n.archivedAt){ti(e);return}let r=bs(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Qr(t,!1);return}let o=await ae(n.leadBox);if(!o){Qr(t,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(Wr(s?.agent)){Qr(t,!1);return}let a=await Se(n.leadBox,{lines:Be}).catch(()=>"");if(fc(s?.agent,a)){Qr(t,!1);return}if(Ur(s?.agent,a)){let c=await rn(n.leadBox).catch(()=>a);if(Jn(s,c)){Qr(t,!1);return}}if(!Qr(t,!0))return;let i=t.items.shift();t.idleSince=null,await vv(n,s,o,i.text,i.kind??null,i.attachments??null),await Z()}finally{t.flushing=!1}}}async function xv(e,t,n,r){let o=Wr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(hs(e,[n]),await Z()),Lb(e.id,o))try{await I(e,{type:"usage.cap.hit",actor:"system",provider:t?.agent??null,boxId:e.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${t?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function yP(e,t){let n=await Se(e.leadBox,{lines:Be}).catch(()=>"");if(!Ur(t?.agent,n))return!1;let r=await rn(e.leadBox).catch(()=>n);return!!Jn(t,r)}async function vv(e,t,n,r,o=null,s=null){await xv(e,t,r,o);let a=Va.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=wv(r,e.pendingHandoffHydrate.artifact||eo),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=Qx(u.map(m=>m.rel));i=i.trim()?`${i.trim()}
|
|
158
|
+
(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 nm(e){return e.role==="user"&&IP.test((e.text||"").trim())}async function z(){return At||(Vp||=(async()=>{try{At=JSON.parse(await H.readFile(CS,"utf8"))}catch{At={tracks:[]}}finally{Vp=null}return At})(),Vp)}async function X(){await at(CS,At)}function OP(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
159
|
+
`;return zp=zp.then(()=>H.mkdir(rn,{recursive:!0})).then(()=>H.appendFile(EP,n)).catch(()=>{}),zp}async function on({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let f=null;try{f=JSON.parse(await H.readFile(Jp,"utf8"))}catch{}if(f?.latest===t&&f?.current===e)return{announced:0,deduped:!0}}let l=await z(),u=0;for(let f of l.tracks)f.archivedAt||!f.leadBox||(he(f,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await X().catch(()=>{}),s==="available")try{await H.mkdir(G.dirname(Jp),{recursive:!0}),await H.writeFile(Jp,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}function DS(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=>CP.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 BS(e,t,n,{kind:r}={}){let o=await se(t);if(!o)throw new Error(`box "${t}" is not running`);let s=ci(o.agent);await Xn({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Fe(t,n,{...Gt(o.agent),kind:r||"orchestration"}),await Y({flow:"orchestration",phase:"sent",trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await z(),i=re(a,e);if(!i)return;let c=i.boxes.find(l=>l.name===t);he(i,"orchestration-prompt",{box:t,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await X()}async function MP(e){let t=G.join(So(e),`${e.id}.ndjson`),n;try{n=await H.stat(t)}catch{return null}let r=ES.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 ES.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function NP(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:zf(n.name,n.agent)}async function Et(e,t=null){let n=t??new Map((await $t()).map(i=>[i.name,i])),r=e.boxes.map(i=>{let c=n.get(i.name),l=Mn.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([MP(e),NP(e,r)]);return{...e,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:oi.has(e.id)}}function jS(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),f=l.createdAt-PP,d=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=f&&m.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function LS(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(d=>d.box.sessionId),a=pg(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=jS(u,a,d=>i.has(d));for(let d of f){let p=u.find(m=>m.name===d.box);p&&(p.sessionId=d.sessionId),await og(d.box,d.sessionId),n=!0}}return n}async function ae(){let e=await z();await LS(e)&&await X();let t=new Map((await $t()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Et(n,t)))}async function La(e,t){let n=await z();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function um(e){let t=js(e);if(!t)return null;let n=await z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&ew(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 LS(t)&&await X(),Et(n)):null}async function dm(e){let t=await L(e).catch(()=>[]),n;try{n=Zn(t)}catch{return[]}return xy(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function rm(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(DP[e.agent]||[]).some(n=>n.test(t))}function om(e){let t=(e.text||"").trim();return t?BP.some(n=>n.test(t)):!1}async function Ol(e,t={}){let n=await ne(e);if(!n)return null;let r=new Set([...n.events.filter(b=>b.type==="orchestration-prompt"&&b.prompt).map(b=>String(b.prompt).trim()),...n.events.filter(b=>b.type==="said"&&b.text).map(b=>String(b.text).trim())]),o=xs(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=yp(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...c0(n),...l],d={windows:[...Wy(n),...qy(n,Cc),...a,...u]},m=(await Promise.all(n.boxes.map(b=>cf(b,t)))).flat(),h=m.map(b=>{let P={kind:"message",...b};if(P.role==="assistant"&&(P.text||"").trim()&&P.tools?.length){let Ce=Ta(P.agent).cliTools,We=P.tools.filter(Ht=>!Ce.has(Ht));return{...P,tools:We.length?We:void 0}}return P}).map(b=>{if(b.role!=="user")return b;let P=Rc(b.text);return P?P.kind==="suppress"?null:{...b,note:P,text:fy(P)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!rm(b)).filter(b=>!om(b)).filter(b=>!nm(b)).filter(b=>!Fp(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!Ky(b,d)),g=h.filter(b=>b.failed&&b.text===Dr);if(g.length){let b=new Map;for(let P of g){if(!b.has(P.box)){let Ht=[...(await Ee(P.box,{lines:Le}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(te=>te[1]);b.set(P.box,Ht.length?Ht[Ht.length-1]:null)}let Ce=b.get(P.box);Ce&&(P.text=`\u25A0 ${Ce}`)}}let x=await Promise.all(n.boxes.map(b=>vb(b,t)));await qx(n,x.flat());let{agentTurnBoxes:k}=await Hx(n,h);for(let b of n.boxes)k.has(b.name)&&b.cwd&&tc(b.cwd);await yk(n),await h0(n,h).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await Gx(n,b),b.agent==="cursor"&&b.state==="live"&&await Xx(n,b);let A=await Promise.all(n.boxes.map(b=>Sb(b,t)));await Wx(n,A.flat().filter(b=>!Yp(b.box,b.ts,u)));let R=await L(n),M=c&&i.viewId?i.viewId:null,N=$k(R),C=Zn(R).filter(b=>!(b.kind==="message"&&(rm(b)||om(b)||nm(b)||Fp(b)))&&!(b.kind==="view-card"&&M&&b.viewId===M)),T=Ok(C,N),j=C.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!T.has(b)&&Yp(b.box,b.ts,N))),Z=Hd(R,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=>!Yp(b.anchorBox,b.anchorTs??b.ts,u)),fe=by(ky([...j,...Z].filter(b=>b.ts!=null).sort((b,P)=>b.ts-P.ts))),ie=LP(n),D=ie.size?fe.filter(b=>b.kind==="event"||!ie.has(b.box??b.anchorBox)):fe,I=n.boxes.find(b=>b.name===n.leadBox)||null,F=I?await aa(I.cwd,I.sessionId,{fallbackNewest:!1}):null;if(!F&&I){let b=[...R].reverse().find(P=>P.type==="context.usage"&&P.boxId===I.name);b?.payload?.window&&(F={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=F?.model||Se;He&&(!pe?.ts||He.at>=pe.ts)&&(le=He.to);let W=I?await Ee(I.name,{lines:Le}).catch(()=>""):"",J=I&&Vr(I.agent,W)?await fn(I.name).catch(()=>W):W,ee=I?or(I,J):null;if(ee&&I){let b=Dc(I,ee,J);if(b){let P=Lb(I.name,ee.promptId);P.action!=="give-up"&&(P.action==="send"&&($e(I.name,b.keys,{kind:"auto-reject"}).catch(()=>{}),P.first&&_(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:I.name,provider:I.agent,payload:{ts:Date.now(),promptId:ee.promptId,rule:b.id}}).catch(()=>{})),ee=null)}}!ee&&I&&!pa(I.agent,W)&&!ao(n.id).length&&(ee=Fd(D,{leadBoxId:I.name,provider:I.agent,isAnswered:b=>R.some(P=>P.type==="prompt.answered"&&P.payload?.promptId===b)})),ee&&bk(n,I,ee);let Xe=D.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),w=!!I&&al(I.name,I.agent,W)&&!ee;I&&!I.readyAt&&(w||ee||W&&vc(I.agent,W))&&(I.readyAt=Date.now(),await X());let v=!!I&&!I.readyAt&&Date.now()-(n.createdAt||0)<jP;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&I&&vc(I.agent,W)&&!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:I.name,provider:I.agent,payload:{provider:I.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=hl(n.id);P&&(he(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:P.finishedAt}),await X())}else if(ee||b>a0&&!w&&!i.viewId||b>gp){let P=ee?"needs-input":b>gp?"timeout":"no-view";hl(n.id,{error:P})&&await _(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:I?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:P}}).catch(()=>{})}else ve={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Xe,context:F?{pct:F.pct,occupancy:F.occupancy,window:F.window,windowKnown:F.windowKnown,model:F.model}:null,models:{current:le,options:dk(I?.agent)},pendingPrompt:ee,working:w,stopping:US(n.id),leadStarting:v,handoff:$,view:ve,queued:ao(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function LP(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 fm(e,t){let n=await ne(e);if(!n)return null;let r=co(e).find(f=>f.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await L(n),a=Zn(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Hd(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await Ee(o,{lines:Le}).catch(()=>"");u=al(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Cl(e,{promptId:t,answers:n,text:r}={}){let o=re(await z(),e);if(!o)throw new Error(`track "${e}" not found`);di(e,"prompt-answer");let s=o.boxes.find(m=>m.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await se(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await hf(s);if(i||(i=await FP(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let m=typeof r=="string"?r.trim():"";if(!m)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(m)||m.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Fe(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await $e(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let m=wy(i,n||[],typeof r=="string"?r:"");if(!m)throw new Error("no answer selected \u2014 pick an option or reply in chat");await _t(o.id,m,{kind:"prompt-answer"}),c={mode:"say",value:m,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let m=Pb(i,n||[]);await $e(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=Ug(i,n||[]);await Fe(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:m,count:1}}if(i.answerMode==="say"){let m=i.questions||[];for(let h=0;h<m.length;h++){let g=m[h],x=new Set(Array.isArray(n?.[h])?n[h]:[]),k=[...x].map(R=>g.options?.[R]).filter(Boolean).map(R=>({label:R.label,description:R.description||""})),A=(g.options||[]).map((R,M)=>x.has(M)?null:R?.label).filter(Boolean);Cf(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]:[]),f=[...u].map(m=>l.options?.[m]).filter(Boolean).map(m=>({label:m.label,description:m.description||""})),d=(l.options||[]).map((m,h)=>u.has(h)?null:m?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:f,rejected:d,...p?{control:p}:{}},await Y({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Cf(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function FP(e,t){if(!t)return null;let n=await L(e).catch(()=>[]);return Fd(Zn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function pm(e,{promptId:t,questionIndex:n}={}){let r=re(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await se(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await hf(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=Db(a,n);return c.length?(await $e(r.leadBox,c,{kind:"navigate-prompt"}),await Y({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Ml(e){let t=ei.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},ei.set(e,t)),t}function ao(e){return(ei.get(e)?.items??[]).map(t=>({...t}))}function ui(e){let t=ei.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function mm(e){let t=ui(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=re(await z(),e);if(o)return r=gs(o,n),await X(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function hm(e){let t=Ml(e);t.timer||(t.timer=setInterval(()=>HP(e).catch(()=>{}),UP))}async function HP(e){let t=ei.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=re(await z(),e);if(!n||n.archivedAt){ui(e);return}let r=xs(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){oo(t,!1);return}let o=await se(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await _m(o)&&(o=await si(n,s,"queue")?await se(n.leadBox):null,await X()),!o){oo(t,!1);return}if(Jr(s?.agent)){oo(t,!1);return}let a=await Ee(n.leadBox,{lines:Le}).catch(()=>"");if(Sc(s?.agent,a)){oo(t,!1);return}if(Vr(s?.agent,a)){let c=await fn(n.leadBox).catch(()=>a);if(or(s,c)){oo(t,!1);return}}if(!oo(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await sm(n,s,o,i.text,i.kind??null,i.attachments??null)}catch(c){if(c?.code!=="cli-not-running")throw c;t.items.unshift(i),await si(n,s,"queue-gate")}await X()}finally{t.flushing=!1}}}async function FS(e,t,n,r){let o=Jr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(gs(e,[n]),await X()),tk(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 WP(e,t){let n=await Ee(e.leadBox,{lines:Le}).catch(()=>"");if(!Vr(t?.agent,n))return!1;let r=await fn(e.leadBox).catch(()=>n);return!!or(t,r)}async function sm(e,t,n,r,o=null,s=null){await FS(e,t,r,o);let a=ti.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=PS(r,e.pendingHandoffHydrate.artifact||so),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=yS(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
158
160
|
|
|
159
|
-
${d}`:d}let f=
|
|
161
|
+
${d}`:d}let f=he(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||ws(e,null);try{if(await Xn({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:ci(n.agent),bailOnDialog:!0}),(ti.get(e.id)||0)!==a){!o&&r.trim()&&(gs(e,[r]),await X());let d=new Error("delivery stopped \u2014 the message was handed back as the draft");throw d.interrupted=!0,d}if(await WP(e,t)){let d=Ml(e.id),p={id:ks(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};d.items.unshift(p),hm(e.id);let m=new Error("lead is blocked on a prompt \u2014 message queued");throw m.requeued={id:p.id,position:d.items.length},m}await Fe(e.leadBox,i,{...Gt(t?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(d){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),he(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,...d?.interrupted?{reason:"interrupted"}:{},...d?.requeued?{reason:"requeued"}:{}}),d?.requeued)return{queued:!0,...d.requeued};throw d}await oD(e,t,i)||(await HS(e.leadBox,t?.agent,i.length+8).catch(()=>{}),he(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,reason:"unsubmitted"}),o||gs(e,[r])),c&&await Y({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function wm(e,{actions:t}={}){Ml(e).items.push({id:ks(),text:Ck(t),ts:Date.now(),kind:"ship-notify"}),hm(e)}async function _t(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"||!Uo(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=re(await z(),e);if(!s)throw new Error(`track "${e}" not found`);if(s.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);Dm(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=Uo(t);await FS(s,a,i,n),di(e,"say");let c=await se(s.leadBox);if(c&&await _m(c)?await si(s,a,"say")?(await X(),c=await se(s.leadBox)):c=null:!c&&await pi(s)&&(await X(),c=await se(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Ml(e),u=await Ee(s.leadBox,{lines:Le}).catch(()=>""),f=l.items.length>0||Sc(a?.agent,u);if(!f&&Vr(a?.agent,u)){let p=await fn(s.leadBox).catch(()=>u);f=!!or(a,p)}if(f){let p={id:ks(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),hm(e),n||ws(s,null),await X(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let d;try{d=await sm(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await si(s,a,"say-gate")||(await X(),c=await se(s.leadBox),!c))throw p;d=await sm(s,a,c,i,n,o)}return await X(),d?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:d.position}:{ok:!0,leadBox:s.leadBox}}async function gm(e,{hint:t}={}){let n=re(await z(),e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it first`);let r=yp(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";i0(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${_S}
|
|
160
162
|
|
|
161
|
-
What to preview: ${String(t).trim()}`:
|
|
163
|
+
What to preview: ${String(t).trim()}`:_S;try{await _t(e,a,{kind:"spin-up-view"})}catch(i){throw hl(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Nl(e){let t=re(await z(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);ti.set(e,(ti.get(e)||0)+1);let n=ui(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=hs.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=gs(t,r),await X()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=JP(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,gr(qP)]);let a=re(await z(),e)||t;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}function US(e){let t=hs.get(e);return!!t&&!t.canceled}function di(e,t){let n=hs.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function ym(e){di(e,"track-ended"),ti.delete(e),ui(e)}function JP(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}),hs.set(e,r),(async()=>{let s=re(await z(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await se(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<KP&&!r.canceled;g++){await $e(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let x of GP){await gr(x);let k=await Ee(s.leadBox,{lines:Le}).catch(()=>"");if(!pa(a?.agent,k)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<zP&&!r.canceled&&(await gr(VP),!r.canceled);g++){let x=await Ee(s.leadBox,{lines:Le}).catch(()=>"");if(!pa(a?.agent,x)){c=!0;break}await $e(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=re(await z(),e)||s,f=u?.boxes.find(g=>g.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await QP(u,f).catch(()=>null),d||(p=await nD(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await XP(u).catch(()=>null):null,h=[d||p,...r.queuedTexts].filter(Boolean);u&&h.length&&(r.restored=gs(u,h),await X()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await YP(u||s,f,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await Y({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),hs.get(e)===r&&hs.delete(e),o()}),r}async function YP(e,t,n){await _(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function XP(e){let t=bl(await L(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await _(e,{type:n?"pr.exists.answered":"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.box,payload:{ts:Date.now(),offerId:t.offerId,...n?{choice:"dismiss"}:{action:"dismiss",actions:null},reason:"interrupt"}}),t.offerId}async function QP(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 ZP){await gr(r);let o=await Ee(e.leadBox,{lines:Le}).catch(()=>""),s=ya(t.agent,o);if(s&&$d(n.text,s))return await HS(e.leadBox,t.agent,n.text.length+8),he(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function nD(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>eD||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ne(r.text))return null;for(let s of tD){await gr(s);let a;try{a=await cf(t,{})}catch{return null}if(oy(a,r))return null;let i=await Ee(e.leadBox,{lines:Le}).catch(()=>"");if(ya(t.agent,i))return null}return he(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function oD(e,t,n){if(t?.agent!=="claude")return!0;for(let r=0;r<=Xp;r++){let o=!1;for(let s of rD){await gr(s);let a=await Ee(e.leadBox,{lines:Le}).catch(()=>""),i=sy(t.agent,n,a,{busy:pa(t.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Xp&&(await $e(e.leadBox,["Enter"],{kind:"submit-retry"}),Y({flow:"say",phase:"submit-retry",trackId:e.id,sessionName:e.leadBox,agent:t.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return Y({flow:"say",phase:"submit-failed",trackId:e.id,sessionName:e.leadBox,agent:t.agent,retries:Xp,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function HS(e,t,n){for(let r=0;r<3;r++){await $o(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await gr(200);let o=await Ee(e,{lines:Le}).catch(()=>""),s=ya(t,o);if(!s)return!0;n=s.length+8}return!1}function ws(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 gs(e,t){let n=[],r=o=>n.some(s=>Ne(s).includes(Ne(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?ws(e,n.join(`
|
|
162
164
|
|
|
163
|
-
`),{source:"restore"})?.text??null:null}function
|
|
165
|
+
`),{source:"restore"})?.text??null:null}function aD(e,t){let n=Ne(t);if(!n)return!1;if(ao(e.id).some(o=>Ne(o.text)===n))return!0;let r=e.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>sD)break;if(!(s.type!=="said"||!s.text||Ne(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function bm(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=re(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=Uo(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&aD(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;Ne(o).includes(Ne(i))?ws(r,o):ws(r,`${i}
|
|
164
166
|
|
|
165
|
-
${o}`,{source:"restore"})}else
|
|
166
|
-
`);return`${
|
|
167
|
+
${o}`,{source:"restore"})}else ws(r,o);return await X(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function km(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=re(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await se(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(f=>f.name===r.leadBox)?.agent||o.agent||"claude",i=fk(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await aa(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Xn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ci(o.agent)});let u=await pk(a,r.leadBox,i);return he(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await X(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function WS(e){for(let t of e){if(!t||!await Ho(t))continue;let n=await Wb(t).catch(()=>null);if(!n)continue;if((await H.stat(G.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function gt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:G.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function as(e){let t=[];for(let n of gt(e?.workspace)){if(!n?.dir)continue;let r=await Aa(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Ea(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function qS(e){let t=e;typeof t=="string"&&!G.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await bo(t)??await Vi(t));let n=await WS([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 iD(e,t,n,r){let o=`track/${Fm(t)}-${e}`,s=[];try{await H.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await qS(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await me(c).catch(()=>null),u=G.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${G.basename(c)}-${p}`;a.add(u);let f=G.join(n,u),d=await Ko(c);await qo(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await fi(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 xm(e,t){let n=await z(),r=re(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await qS(typeof t=="string"?t.trim():t);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await me(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=G.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${G.basename(s)}-${d}`;let l=G.join(o.dir,c),u=await Ko(s);await qb(s,o.branch)?await Bc(s,l,o.branch):await qo(s,l,o.branch,u);let f={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),f],await fi(o.dir,o.repos,o.branch),he(r,"retagged",{action:"add",repo:a??s,name:c,workspace:KS(o)}),await X(),f}async function Sm(e,t){let n=await z(),r=re(n,e);if(!r)throw new Error(`track "${e}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===t||c.repo===t||c.base===t));if(!s)throw new Error(`repo "${t}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of wt.values())if(c.trackId===e&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await Aa(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Ea(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 fi(o.dir,o.repos,o.branch),he(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:KS(o)}),await X(),{removed:s.name}}async function cD(e,t,{repo:n,repos:r,cwd:o}={}){await H.mkdir(Za,{recursive:!0});let s=G.join(Za,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return iD(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!G.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await bo(n)??await Vi(n));let i=await WS([n,o,_P]);if(i){let c=`track/${Fm(t)}-${e}`;try{let l=await Ko(i);await qo(i,s,c,l),await ri(s);let u=await me(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 Sf(s),await ri(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ri(e){try{await H.writeFile(G.join(e,"AGENTS.md"),Xa,{flag:"wx"})}catch{return}await fD(e,["/AGENTS.md"])}function dD(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
168
|
+
`);return`${uD}
|
|
167
169
|
|
|
168
170
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
169
171
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -175,20 +177,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
175
177
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
176
178
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
177
179
|
or preview an app, run it from that app's own repo directory.
|
|
178
|
-
`}async function
|
|
179
|
-
`).includes(s));o.length&&await
|
|
180
|
+
`}async function fi(e,t,n){let r=dD(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await H.writeFile(G.join(e,o),r).catch(()=>{})}async function VS(e){let{stdout:t}=await OS("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return G.resolve(e,t.trim())}async function fD(e,t){try{let n=await VS(e);await H.mkdir(G.dirname(n),{recursive:!0});let r=await H.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
181
|
+
`).includes(s));o.length&&await H.appendFile(n,(r.endsWith(`
|
|
180
182
|
`)||!r?"":`
|
|
181
183
|
`)+o.join(`
|
|
182
184
|
`)+`
|
|
183
|
-
`)}catch{}}async function
|
|
185
|
+
`)}catch{}}async function pD(e,t){try{let n=await VS(e),r=await H.readFile(n,"utf8"),o=r.split(`
|
|
184
186
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
185
|
-
`).length&&await
|
|
186
|
-
`))}catch{}}async function
|
|
187
|
+
`).length&&await H.writeFile(n,o.join(`
|
|
188
|
+
`))}catch{}}async function vm(){let e={refreshed:0,removed:0,codexGlobal:null};try{await H.mkdir(rn,{recursive:!0}),await H.writeFile(G.join(rn,"CLAUDE.md"),lD).catch(()=>{}),await H.writeFile(G.join(rn,"AGENTS.md"),Xa).catch(()=>{});let t=process.env.CODEX_HOME||G.join(process.env.HOME||"/tmp",".codex");if((await H.stat(t).catch(()=>null))?.isDirectory()){let r=G.join(t,"AGENTS.md"),o=await H.readFile(r,"utf8").catch(()=>null);o===null?(await H.writeFile(r,Qp,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):Zp(o)&&o!==Qp&&(await H.writeFile(r,Qp).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Za,bs]){let o=await H.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=G.join(r,s.name),i=G.join(a,"AGENTS.md"),c=await H.readFile(i,"utf8").catch(()=>null);c!==null&&Zp(c)&&c!==Xa&&(await H.writeFile(i,Xa).catch(()=>{}),e.refreshed++);let l=G.join(a,"CLAUDE.md"),u=await H.readFile(l,"utf8").catch(()=>null);u!==null&&Zp(u)&&(await H.rm(l,{force:!0}).catch(()=>{}),await pD(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function zS(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 Pl({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 li(o,s),i=await z(),c=ks().slice(0,8),l=await cD(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(h=>h.name))),f=`${e.trim()} lead`;u.has(fc(f))&&(f=`${e.trim().slice(0,50).trim()} ${c} lead`);let d=await zn(f,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0});ma({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let p={id:c,name:e.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Cm(d,"lead","live")],events:[],createdAt:Date.now()};return he(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:m,base:h,baseRef:g,repo:x})=>({name:m,base:h,baseRef:g,repo:x}))}:{}}}),i.tracks.push(p),await X(),Et(p)}async function Tm({provider:e="claude",fresh:t=!1}={}){if(!mD.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 Et(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await io(r.id).catch(()=>{})}let o=(await li(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=ks().slice(0,8),a=G.join(rn,"setup",s);await H.mkdir(a,{recursive:!0});let i=await zn(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&ma({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:[Cm(i,"lead","live")],events:[],createdAt:Date.now()};return he(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await z()).tracks.push(c),await X(),Et(c)}async function Am(e,t,n){await Ku(t.cwd).catch(()=>{}),await m0(e).catch(()=>{});let r=ys.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),ys.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of co(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Wm(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 Ot(a.name).catch(()=>{})}async function Em(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 D0(s),ym(e),await Am(e,s,"track deleted"),n?await pS(e).catch(()=>{}):await fS(e).catch(()=>{}),await wk(),await zS(s),t&&await B0(e).catch(()=>{}),t?await eS(e).catch(()=>{}):await Q0(s).catch(()=>{}),he(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await X(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function io(e){let t=await z(),n=re(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is already archived`);return n.archivedAt=Date.now(),ym(e),he(n,"archived",{boxes:n.boxes.length}),await X(),n.kind==="setup"?(await JS(e),Et(n)):(oi.add(e),gD(e).catch(()=>{}).finally(()=>oi.delete(e)),{...await Et(n),archiving:!0})}async function gD(e){try{await yD(e)}catch{}await JS(e).catch(()=>{})}async function JS(e){let t=await z(),n=re(t,e);if(!(!n||!n.archivedAt)){await Am(e,n,"track archived");for(let r of n.boxes)Mn.has(r.state)||(r.state="stopped");await X()}}async function yD(e){let t=await z(),n=re(t,e);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await se(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Uy(n.leadBox,i.cwd,{timeoutMs:wD})),s="lead-cli"}catch{}if(r||(r=tb(n,await L(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await _(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await X()}async function kn(e,t){let n=await qe(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await YS(n);if(r.length){for(let o of r)await Yr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await io(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function YS(e){return(await Xr().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 xn(e,t){return Dl(e,t,"merged")}async function Dl(e,t,n="merged"){let r=await qe(e,t).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await YS(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Hk(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await _t(r.trackId,XS(a),{kind:"merge-notify"}),!0}catch{return!1}}function XS(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 ${IS[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 ${IS[i.outcome]??i.outcome}`).join(`
|
|
187
189
|
`),a=n.length?r.length?`This is an automated merge notification: pick up whatever follow-up work the merged PR${n.length===1?"":"s"} unblock${n.length===1?"s":""} \u2014 and note the unmerged one${r.length===1?"":"s"}: that work did NOT land, so check whether it still needs to ship. If nothing needs doing, say so briefly.`:"This is an automated merge notification: pick up whatever follow-up work these merges unblock (dependent tasks, cleanup, next steps that were waiting on them). If nothing needs doing, say so briefly.":"This is an automated notification: NONE of the PRs landed \u2014 the work did not ship. Check whether it still needs to go out another way. If not, say so briefly.";return`${o}
|
|
188
190
|
${s}
|
|
189
|
-
${a}`}async function Rl(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=Kp.get(t.name);if(n)return n;let r=zP(e,t).finally(()=>Kp.delete(t.name));return Kp.set(t.name,r),r}async function zP(e,t){if(e.archivedAt||e.transferredTo||cr.has(t.state)||await ae(t.name))return!1;let n=Zp(t.agent,t.sessionId),r=await tc(n);if(r===xo)return fe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===vo&&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 Ln(t.name,t.cwd,{agentCmd:n,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ps(o.hookToken),ia({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",fe(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return fe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function wm(e){let t=await K(),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,Ya.delete(e),fe(n,"unarchived",{}),await Rl(n),await Z(),Yt(n)}async function gm(){let e=await K(),t=new Set((await Tt()).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 Dt(a.name).catch(()=>{}),fe(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!cr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Z(),r}async function $l(e){let t=await K(),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`);Sm(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(cr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ae(n.leadBox)){let a=await Se(n.leadBox,{lines:Be}).catch(()=>"");if(fc(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Dt(n.leadBox),r.state="stopped"}let s=await Rl(n);if(await Z(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Yt(n)}async function ym(e,t,n){let r=await te(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await B(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await $l(e),await I(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function Ol(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 kl(e,t){let n=await K(),r=re(n,e);r&&(r.handoff={...r.handoff||{},...t},await Z())}function Nv(e,t=Date.now()){let n=e?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=e.boxes?.find?.(i=>i.name===e.leadBox)||null;return(o?e.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&e.leadBox===n.toBox)?{...n,step:"done",finishedAt:t,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${r}"; lead "${e.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function km(){let e=await K(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=Nv(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await 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(),f=Ol(l),d=f[f.length-1],m=d.agent,p=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:eo,at:u},fe(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:m,toAgent:r.toAgent,fromSessionId:p,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await Y({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await Y({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Dt(i).catch(()=>{}),a&&await Dt(a).catch(()=>{})}return t&&await Z(),t}async function Cl(e,t,n=!0,r=null){let o=ws.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=re(await K(),e);if(!s)throw new Error(`track "${e}" not found`);ni(e,"handoff");let a=await ei(t,n),i=Wr(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:gv(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ws.set(e,l),await kl(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),JP(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 kl(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 JP(e,t,n,r,o){let s=async(A,V={})=>{e.step=A,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${A}`),await kl(e.trackId,{step:A,toBox:e.toBox}),await Y({flow:"lead-handoff",phase:A,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:V.sessionName,...V})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await K(),i=re(a,e.trackId)?.boxes.find(A=>A.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",Wr(c))e.checkpoint="skipped-capped";else try{await Bd(e.fromBox,{timeoutMs:hv})}catch(A){e.checkpoint="reconstructed",await Y({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:A?.message||String(A)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Dd(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await Wd(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox}),await Hn({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Qa(o)});let m=await Se(e.fromBox,{lines:Be}).catch(()=>"");if(!pc(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let A=Zp(c,l,{skipPermissions:e.skipPermissions});await Wd(e.fromBox,{agentCmd:A,agent:c,resumeSessionId:l,pinned:u});let V=await K(),O=re(V,e.trackId);if(O){let M=O.boxes.find(T=>T.name===e.fromBox);M&&(M.state="live"),fe(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 Z()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let p=await K(),w=re(p,e.trackId);if(!w)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=w.boxes.find(A=>A.name===e.fromBox);if(!y)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let b=Date.now(),v=Ol(y),R=v[v.length-1];R.endedAt=b,e.reason&&(R.endReason=e.reason),v.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:b,endedAt:null}),y.agent=o,y.sessionId=d.sessionId,y.pinned=d.pinned,y.state="live",y.role="lead",w.pendingHandoffHydrate={artifact:e.statusArtifact||eo,at:b},fe(w,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:v.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await Z(),await s("done",{sessionName:e.fromBox}),await kl(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function vm(e){let t=Xp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await K(),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=ws.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of dt.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 Xp.set(e,s),YP(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 YP(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&&Wr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Bd(t.leadBox,{timeoutMs:hv})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Dd(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 ft(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Tn(u.dir,["push","origin",o],Yp);let{stdout:d}=await Tn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
190
|
-
`)[0]}s.push(f)}e.step="wip";let a=await
|
|
191
|
-
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await
|
|
191
|
+
${a}`}async function _m(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<oa()?!1:dn(e.name)}async function si(e,t,n){return!t||e.archivedAt||Mn.has(t.state)?!1:(await Y({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Ot(t.name).catch(()=>{}),t.state="stopped",pi(e))}async function pi(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=em.get(t.name);if(n)return n;let r=bD(e,t).finally(()=>em.delete(t.name));return em.set(t.name,r),r}async function bD(e,t){if(e.archivedAt||e.transferredTo||Mn.has(t.state)||await se(t.name))return!1;let n=lm(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),r=await dc(n);if(r===_o)return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===Io&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await zn(t.name,t.cwd,{agentCmd:n,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=js(o.hookToken),ma({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",he(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function Im(e){let t=await z(),n=re(t,e);if(!n)throw new Error(`track "${e}" not found`);if(!n.archivedAt)throw new Error(`track "${e}" is not archived`);return delete n.archivedAt,oi.delete(e),he(n,"unarchived",{}),await pi(n),await X(),Et(n)}async function Rm(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>am)throw new Error(`name too long (max ${am} 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 Et(o);let s=o.name;return o.name=n,he(o,"renamed",{from:s,to:n}),await X(),Et(o)}async function $m(){let e=await z(),t=new Set((await $t()).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 Ot(a.name).catch(()=>{}),he(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Mn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await X(),r}async function Bl(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`);Dm(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(Mn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await se(n.leadBox)){let a=await Ee(n.leadBox,{lines:Le}).catch(()=>"");if(Sc(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ot(n.leadBox),r.state="stopped"}let s=await pi(n);if(await X(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Et(n)}async function Om(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 Bl(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 jl(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 Il(e,t){let n=await z(),r=re(n,e);r&&(r.handoff={...r.handoff||{},...t},await X())}function ZS(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 Mm(){let e=await z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=ZS(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await se(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),f=jl(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:so,at:u},he(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await Y({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await Y({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ot(i).catch(()=>{}),a&&await Ot(a).catch(()=>{})}return t&&await X(),t}async function Ll(e,t,n=!0,r=null){let o=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=re(await z(),e);if(!s)throw new Error(`track "${e}" not found`);di(e,"handoff");let a=await li(t,n),i=Jr(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await se(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:DS(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ys.set(e,l),await Il(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),kD(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 Il(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 kD(e,t,n,r,o){let s=async(M,N={})=>{e.step=M,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${M}`),await Il(e.trackId,{step:M,toBox:e.toBox}),await Y({flow:"lead-handoff",phase:M,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:N.sessionName,...N})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await z(),i=re(a,e.trackId)?.boxes.find(M=>M.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",Jr(c))e.checkpoint="skipped-capped";else try{await Kd(e.fromBox,{timeoutMs:NS})}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 Gd(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await Zd(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=Gg(d.launchCmd);await Xn({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:ci(o),launchEcho:p}).catch(M=>{if(M?.code!=="cli-not-running")throw M});let m=_d(await Ee(e.fromBox,{lines:Le}).catch(()=>""),p);if(!vc(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let M=lm(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Zd(e.fromBox,{agentCmd:M,agent:c,resumeSessionId:l,pinned:u});let N=await z(),C=re(N,e.trackId);if(C){let T=C.boxes.find(j=>j.name===e.fromBox);T&&(T.state="live"),he(C,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await X()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let h=await z(),g=re(h,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let x=g.boxes.find(M=>M.name===e.fromBox);if(!x)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let k=Date.now(),A=jl(x),R=A[A.length-1];R.endedAt=k,e.reason&&(R.endReason=e.reason),A.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:k,endedAt:null}),x.agent=o,x.sessionId=d.sessionId,x.pinned=d.pinned,x.state="live",x.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||so,at:k},he(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:A.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await X(),await s("done",{sessionName:e.fromBox}),await Il(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Pm(e){let t=cm.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await z(),r=re(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of wt.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return cm.set(e,s),xD(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 xD(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await se(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Jr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Kd(t.leadBox,{timeoutMs:NS})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Gd(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of gt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Cn(u.dir,["push","origin",o],im);let{stdout:d}=await Cn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
192
|
+
`)[0]}s.push(f)}e.step="wip";let a=await Wp({id:t.id,cwd:t.cwd}),i=await z(),c=re(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&jl(l),await X(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:f,baseSha:d})=>({name:u,repo:f,baseSha:d})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:f,error:d})=>({name:u,repo:f,error:d})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Dm(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 Fl(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await z(),o=re(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};ym(e),await Am(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();for(let i of o.boxes){Mn.has(i.state)||(i.state="stopped");let c=Array.isArray(i.epochs)?i.epochs[i.epochs.length-1]:null;c&&typeof c=="object"&&c.endedAt==null&&(c.endedAt=s,c.endReason==null&&(c.endReason=n==="recover"?"node-offline":"node-transfer"))}let a=await qp({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,he(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await X(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function Bm(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Fl(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 RS(e,t,n){try{await Bc(e,t,n)}catch{await Cn(e,["worktree","prune"]).catch(()=>{}),await Bc(e,t,n)}return t}async function jm(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(tm.has(t))throw new Error("an adopt is already running for this Track");tm.set(t,{startedAt:Date.now()});try{return await SD({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{tm.delete(t)}}async function SD({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:f=[],createdAt:d=null,leadBox:p=null,reason:m=null}){let h=await li(n,r),g=await z(),x=re(g,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await qp({id:x.id,cwd:x.cwd}).catch(()=>{}),await zS(x),g.tracks.splice(g.tracks.indexOf(x),1),await X()),await H.mkdir(Za,{recursive:!0});let k=G.join(Za,e);await H.rm(k,{recursive:!0,force:!0}).catch(()=>{});let A=null,R=!1,M=[];try{let N=[];if(i)for(let W of Array.isArray(c)?c:[]){let J=typeof W=="string"?W:W?.repo;if(!J||!/^[^/\s]+\/[^/\s]+$/.test(J))continue;let ee=await bo(J)??await Vi(J);await Cn(ee,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],im),N.push({slug:J,base:ee,name:typeof W=="object"?W?.name??null:null})}if(i&&N.length===1){let{base:W,slug:J}=N[0];await RS(W,k,i),M.push({base:W,dir:k}),await ri(k);let ee=await Ko(W);A={dir:k,base:W,branch:i,kind:"worktree",baseRef:ee,repo:J}}else if(i&&N.length>=2){await H.mkdir(k,{recursive:!0});let W=new Set([".bostrat"]),J=[];for(let ee of N){let Xe=ee.name||G.basename(ee.base);W.has(Xe)&&(Xe=ee.slug.replace("/","-"));for(let v=2;W.has(Xe);v++)Xe=`${G.basename(ee.base)}-${v}`;W.add(Xe);let y=G.join(k,Xe);await RS(ee.base,y,i),M.push({base:ee.base,dir:y});let w=await Ko(ee.base);J.push({name:Xe,dir:y,base:ee.base,baseRef:w,repo:ee.slug})}await fi(k,J,i),A={dir:k,kind:"multi",branch:i,repos:J}}else R=!0,await H.mkdir(k,{recursive:!0}),await Sf(k),await ri(k),A={dir:k,base:null,branch:null,kind:"repo"};let C=!1,T=null;if(l?.sha&&A.kind==="worktree")try{await Cn(k,["fetch","origin",l.ref||Hp(e)],im);let{stdout:W}=await Cn(k,["rev-parse","FETCH_HEAD^"]),{stdout:J}=await Cn(k,["rev-parse","HEAD"]);W.trim()===J.trim()?(await Cn(k,["read-tree","--reset","-u","FETCH_HEAD"]),await Cn(k,["reset","-q"]),C=!0):T="snapshot does not parent the fetched branch head"}catch(W){T=String(W?.message||W).split(`
|
|
193
|
+
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await TS({id:e,cwd:k}).catch(()=>({restored:!1}));let j=G.join(k,so),Z=null;(await sc(k)).restored?Z="checkpoint":typeof u=="string"&&u.trim()&&(await H.mkdir(G.dirname(j),{recursive:!0}),await H.writeFile(j,u),Z="brief");let ie=new Set(g.tracks.flatMap(W=>(W.boxes||[]).map(J=>J.name))),D=null;p&&!ie.has(p)&&!await se(p).catch(()=>null)?D=p:(D=`${t.trim()} lead`,ie.has(fc(D))&&(D=`${t.trim().slice(0,50).trim()} ${e} lead`));let I=await zn(D,k,{agentCmd:h,agent:n});ma({cwd:I.cwd,sessionName:I.name,agent:n,agentCmd:h});let F=Date.now(),pe=Cm(I,"lead","live"),Se=(Array.isArray(f)?f:[]).filter(W=>W&&typeof W=="object").map(W=>({...W})),He=Se[Se.length-1];He&&He.endedAt==null&&(He.endedAt=F,He.endReason==null&&(He.endReason=o==="recover"?"node-offline":"node-transfer")),Se.push({agent:n,sessionId:I.sessionId??null,pinned:I.pinned===!0,startedAt:F,endedAt:null}),pe.epochs=Se;let le={id:e,name:t.trim(),cwd:I.cwd,workspace:A,leadBox:I.name,boxes:[pe],events:[],createdAt:Number.isFinite(d)?d:F,...Z?{pendingHandoffHydrate:{artifact:so,at:F}}:{}};return he(le,"transferred-in",{box:I.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,branch:A.branch??null,wip:C,epoch:Se.length-1,name:le.name,cwd:le.cwd,workspace:{base:A.base??null,branch:A.branch??null,kind:A.kind,...A.baseRef?{baseRef:A.baseRef}:{},...A.repo?{repo:A.repo}:{},...A.repos?{repos:A.repos.map(({name:W,base:J,baseRef:ee,repo:Xe})=>({name:W,base:J,baseRef:ee,repo:Xe}))}:{}}}),g.tracks.push(le),await X(),{...await Et(le),adopt:{wipApplied:C,wipSkipped:T,codeless:R,artifactSource:Z}}}catch(N){for(let C of M)await pt(C.base,C.dir).catch(()=>{});throw await H.rm(k,{recursive:!0,force:!0}).catch(()=>{}),N}}async function QS(e,t,n){let r=await z(),o=re(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await X())}async function ms(e,t){e.step=t,await mi(e)}function ev(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 tv(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function ED(e,t,n){if(!tv(n)||wt.has(t)||!await se(n.box).catch(()=>null))return!1;try{await H.access(n.dir)}catch{return!1}let o=await se(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return wt.set(t,s),rv(s,o).catch(a=>nv(s,a)),!0}async function Lm(){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 ED(n,r,o)){t=!0,he(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await Y({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=ev(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Mn.has(i.state)&&(i.state="canceled"),he(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await Y({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await $S(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Mn.has(r.state)||wt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?G.join(bs,`${n.id}-${r.subtaskId}`):null;await Y({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await $S(n,r.name,o)}}return t=await _D(e)||t,t&&await X(),t}async function $S(e,t,n){if(t&&await Ot(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of gt(e.workspace)){let s=G.join(n,o.name);await pt(o.base,s).then(()=>!0,()=>!1)||await pt(o.dir,s).catch(()=>{})}await H.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await se(e.leadBox).catch(()=>null);r?.cwd&&await pt(r.cwd,n).catch(()=>{})}async function _D(e){let t;try{t=await H.readdir(bs)}catch{return!1}let n=new Set;for(let o of e.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of wt.values())o.dir&&n.add(G.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=G.join(bs,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of gt(a.workspace))await pt(i.base,G.join(s,i.name)).catch(()=>{});await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await se(a.leadBox).catch(()=>null):null;i?.cwd?await pt(i.cwd,s).catch(()=>{}):await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await Y({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Um(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Uo(n))throw new Error("task (string) required");let s=await li(t,r),a=Jr(t);if(a){let p=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=re(await z(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await se(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=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 Ho(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 Ho(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=ks().slice(0,8),d={id:f,trackId:e,trackName:i.name,agent:t,task:Uo(n),skipPermissions:r,reason:DS(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Fm(i.name)}-${f}`,base:null,repos:u,dir:G.join(bs,`${e}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return wt.set(f,d),await mi(d),ID(d,c,s).catch(p=>nv(d,p)),d}async function nv(e,t){e.failedStep=e.step,e.error=t instanceof ai?"canceled":t?.message||String(t),e.step=t instanceof ai?"canceled":"error",e.finishedAt=Date.now(),await Wm(e).catch(()=>{}),await mi(e).catch(()=>{}),await CD(e).catch(()=>{})}function Hl(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 Hm(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 ID(e,t,n){if(await ms(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Wo(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await mi(e)}else e.base=await Wo(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await ms(e,"spawn"),await H.mkdir(bs,{recursive:!0}),e.repos){await H.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await qo(a.leadDir,G.join(e.dir,a.name),e.branch,a.base);let s=gt((re(await z(),e.trackId)||{}).workspace);await fi(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 qo(t.cwd,e.dir,e.branch,e.base),await ri(e.dir);await RD(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 $D(e,o),await BS(e.trackId,o.name,RP(e.task,!!e.repos),{kind:"subtask"}),await rv(e,t)}async function rv(e,t){await ms(e,"working");let n=G.join(e.dir,_l);if(await ND(n,e),e.canceled)throw new ai;if(await ms(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=G.join(e.dir,l.name);await xf(u,`bostrat: subtask ${e.id} result (auto-committed)`),l.stat=await Tf(l.leadDir,l.base,e.branch,ps),l.files=await Af(l.leadDir,l.base,e.branch,ps);let f=await Ef(l.leadDir,l.base,e.branch,ps);l.applied=f.applied,l.conflicts=f.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(d=>G.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:G.join(l.name,d))),f.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await xf(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`),e.stat=await Tf(t.cwd,e.base,e.branch,ps),e.files=await Af(t.cwd,e.base,e.branch,ps);let s=await Ef(t.cwd,e.base,e.branch,ps);e.applied=s.applied,e.conflicts=s.conflicts}await mi(e);let r=await MD(e,t,n);await ms(e,"resume"),await BS(e.trackId,e.leadBox,$P(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await ms(e,"reap"),await Wm(e),e.step="done";let o=Date.now();await QS(e.trackId,e.id,{...Ul(e),finishedAt:o}),await OD(e,o),e.finishedAt=o}async function RD(e,t){let n=G.join(e.dir,G.dirname(Qa));await H.mkdir(n,{recursive:!0});let r="";try{r=await $g(t.cwd,t.sessionId)}catch{}if(!r)try{r=await H.readFile(G.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
192
194
|
|
|
193
195
|
**Delegated task:** ${e.task}
|
|
194
196
|
|
|
@@ -199,15 +201,15 @@ ${a}`}async function Rl(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)retur
|
|
|
199
201
|
## Conversation so far (lead \u2192 you)
|
|
200
202
|
|
|
201
203
|
${r}
|
|
202
|
-
`;await
|
|
204
|
+
`;await H.writeFile(G.join(e.dir,Qa),o)}async function $D(e,t){let n=await z(),r=re(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:js(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),he(r,"subtask-delegated",{to:t.name,toAgent:e.agent,task:e.task,subtaskId:e.id,skipPermissions:e.skipPermissions,reason:e.reason}),r.subtasks=r.subtasks||{},r.subtasks[e.id]={...r.subtasks[e.id]||{},...Ul(e)},await X())}async function OD(e,t=e.finishedAt){let n=await z(),r=re(n,e.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=e.conflicts.length?"conflict":"merged",o.stat=e.stat),he(r,"subtask-merged",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,stat:e.stat,conflicts:e.conflicts.length,files:e.files,durationMs:t?t-e.startedAt:null}),await X()}async function CD(e){let t=await z(),n=re(t,e.trackId);if(!n)return;let r=e.canceled||e.error==="canceled",o=n.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=r?"canceled":"failed"),he(n,r?"subtask-canceled":"subtask-failed",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,failedStep:e.failedStep||e.step,error:e.error,durationMs:e.finishedAt?e.finishedAt-e.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[e.id]={...n.subtasks[e.id]||{},...Ul(e)},await X()}async function MD(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=G.join(t.cwd,r);await H.mkdir(G.dirname(o),{recursive:!0}),await H.copyFile(n,o)}catch{}return r}async function Wm(e){if(e.dir&&await Ku(e.dir).catch(()=>{}),e.box&&await Ot(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await pt(t.leadDir,G.join(e.dir,t.name)).catch(()=>{});await H.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await se(e.leadBox).catch(()=>null))?.cwd;n&&await pt(n,e.dir).catch(()=>{})}}async function ND(e,t){let n=(t.startedAt||Date.now())+vD;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 gr(TD)}}function gr(e){return new Promise(t=>setTimeout(t,e))}var OS,rn,CS,EP,Za,_P,Rl,ii,wr,so,NS,IP,_l,Qa,RP,$P,At,Vp,re,zp,he,Jp,Yp,CP,Mn,ES,PP,DP,BP,jP,UP,ei,_S,qP,KP,GP,VP,zP,hs,ti,ZP,eD,tD,rD,Xp,sD,KS,ni,GS,lD,Xa,Zp,uD,Qp,mD,oi,hD,wD,IS,em,am,Cm,ys,xs,im,Cn,cm,Nm,tm,vD,TD,bs,ps,wt,AD,co,Ul,mi,Fm,ai,fr=O(()=>{zs();tt();zd();nb();Tc();Wn();bc();ib();Qe();un();Fs();yc();Tb();Br();wf();yf();Ub();Od();kc();Ud();Mt();jc();Un();ha();Hc();mk();Mf();Kx();Wd();Dd();Vx();lp();ue();yl();Ba();Yo();Tp();us();Np();Al();Up();wn();bS();Kp();AS();ac();OS=AP(TP),rn=process.env.BOSTRAT_STATE_DIR||G.join(process.env.HOME||"/tmp",".bostrat"),CS=G.join(rn,"tracks.json"),EP=G.join(rn,"events.jsonl"),Za=G.join(rn,"workspaces"),_P=process.env.BOSTRAT_DEFAULT_REPO||null,Rl={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"}},ii=Object.keys(Rl);wr={at:0,value:null};so=".bostrat/handoff/latest.md",NS=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;IP=/\(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\.\)$/;_l=".bostrat/handoff/subtask-result.md",Qa=".bostrat/handoff/lead-context.md",RP=(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 ${Qa} \u2014 read that first for context. Your scoped task:
|
|
203
205
|
|
|
204
206
|
${e}
|
|
205
207
|
|
|
206
|
-
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${
|
|
208
|
+
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 ${_l} (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 ${Qa} \u2014 read that first for context. Your scoped task:
|
|
207
209
|
|
|
208
210
|
${e}
|
|
209
211
|
|
|
210
|
-
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${
|
|
212
|
+
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 ${_l} 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.`,$P=(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."),At=null,Vp=null;re=(e,t)=>e.tracks.find(n=>n.id===t)||null,zp=Promise.resolve();he=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),OP(e.id,r),gk(e,r),r};Ng(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await z()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(he(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await X().catch(()=>{}))});Jp=G.join(rn,"plane-link","update-notice.json");Yp=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),CP=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Mn=new Set(["merged","conflict","canceled","failed","handed-off"]),ES=new Map;PP=5e3;DP={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};BP=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];jP=10*6e4;UP=1500,ei=new Map;_S="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.";qP=4e3,KP=3,GP=[300,350,400],VP=1e3,zP=12,hs=new Map,ti=new Map;ZP=[250,400,500,600];eD=3e4,tD=[350,650,900];rD=[250,450,700],Xp=2;sD=12e4;KS=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}))});ni="# bostrat workspace",GS=`${ni}
|
|
211
213
|
|
|
212
214
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
213
215
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -475,40 +477,40 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
475
477
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
476
478
|
implementation turn this way, every time. When the operator has already asked you
|
|
477
479
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
478
|
-
`,
|
|
479
|
-
`)),
|
|
480
|
-
`,`${
|
|
480
|
+
`,lD=GS,Xa=GS+gy;Zp=e=>typeof e=="string"&&(e===ni||e.startsWith(`${ni}
|
|
481
|
+
`)),uD="# bostrat multi-repo workspace";Qp=Xa.replace(`${ni}
|
|
482
|
+
`,`${ni}
|
|
481
483
|
|
|
482
484
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
483
485
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
484
486
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
485
487
|
When working anywhere else, ignore everything below.
|
|
486
|
-
`);WP=new Set(["claude","codex","cursor"]);Ya=new Set,qP=e=>Ya.has(e);KP=9e4;lv={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Kp=new Map;bm=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ps(e.hookToken),createdAt:Date.now()});ws=new Map,bs=e=>ws.get(e)||(kt?re(kt,e)?.handoff:null)||null;Yp=6e4,Tn=(e,t,n=15e3)=>fv("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Xp=new Map,xm=e=>Xp.get(e)||null;Gp=new Map;ZP=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,QP=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,gs=L.join(Jt,"worktrees"),ds=[Ka,bl],dt=new Map,eB=e=>dt.get(e)||(kt?kt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,ro=e=>{let t=new Map([...dt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=kt?re(kt,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)},Nl=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});oi=e=>Pv(e.trackId,e.id,Nl(e));_m=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Xa=class extends Error{}});Xe();import pe from"node:path";import Ve from"node:fs";import sF from"node:readline";import{fileURLToPath as aF}from"node:url";import vt from"express";import iF from"node:http";import cF from"node:https";import jh from"node:os";import{WebSocketServer as lF}from"ws";Xe();import $_ from"node-pty";async function Vw(e,t,n={}){if(!Ae(t)){ac(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await jn()){ac(e,`host tmux server not running on ${ct}`),e.close(1011,"tmux server unavailable");return}if(!await ae(t)){ac(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Gw(n.cols,120,zw),o=Gw(n.rows,32,Jw),s;try{s=$_.spawn(j,["-S",ct,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ac(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&zu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(zu(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"&&N_(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),e.on("close",()=>{try{s.kill()}catch{}M_(t)})}var O_=6,C_=250;async function M_(e,t=O_,n=C_){for(let r=0;r<t;r++){if(await Rr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var zw=20,Jw=6;function N_(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=zw&&Number(t)>=Jw}function Gw(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function zu(e,t){try{e.send(JSON.stringify(t))}catch{}}function ac(e,t){zu(e,{type:"error",message:t})}function Yw(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}}At();cc();Ld();Jr();import{createRequire as uF}from"node:module";var uB=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Om({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(uB)&&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 Vv from"node:fs";import dr from"node:fs/promises";import EB from"node:os";import Ke from"node:path";import{spawn as _B}from"node:child_process";import{createRequire as IB}from"node:module";le();Nc();import Kv from"node:fs";import Mm from"node:fs/promises";import pB from"node:http";import En from"node:path";import Dl from"node:os";import{createRequire as mB}from"node:module";import Nm from"ws";import dB from"node:path";import fB from"node:os";var si=process.env.BOSTRAT_STATE_DIR||dB.join(process.env.HOME||fB.tmpdir(),".bostrat");function Cm(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 Uv(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Cm(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function Hv(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 An=(()=>{try{return mB(import.meta.url)("../package.json").version||null}catch{return null}})(),hB=process.env.BOSTRAT_STATE_DIR||En.join(process.env.HOME||Dl.tmpdir(),".bostrat");function oo({env:e=process.env,pkgDir:t=En.resolve(En.dirname(new URL(import.meta.url).pathname),".."),exists:n=Kv.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(En.join(t,"..",".git"))?"git-checkout":"dev"}function ai(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 Wv(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 wB({home:e=process.env.HOME||Dl.homedir(),env:t=process.env}={}){let n=r=>{try{return Kv.statSync(r),!0}catch{return!1}};return{claude:n(En.join(e,".claude",".credentials.json")),codex:n(En.join(t.CODEX_HOME||En.join(e,".codex"),"auth.json"))}}var We=1,qv=1e3,gB=3e4,yB=6e4,bB=1500,kB=500,Bl=100,xB=2e3,vB=25e3,SB=6e4,TB=6e4,AB=6e4;function Gv({url:e,token:t,nodeId:n=Dl.hostname(),name:r=Dl.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=TB,onUpdateAvailable:l=null,onUpdatePolicy:u=null,updatePolicy:f=null,installKind:d=oo(),selfUrl:m=`http://127.0.0.1:${process.env.PORT||3362}`,log:p=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=Hv(e);let w=m.replace(/^http/,"ws"),y=new Set,b=new Map,v=new Map,R=En.join(hB,"plane-link","cursor.json"),A={},V={},O=new Map,M=null,T=!1,q=!1,G=qv,ue=null,Q=null,N=null,$=null,F=null,ce=!1,Re=0;async function De(){try{A=JSON.parse(await Mm.readFile(R,"utf8"))}catch{A={}}}function be(){ce=!0,!N&&(N=setTimeout(async()=>{if(N=null,!!ce){ce=!1;try{await Mm.mkdir(En.dirname(R),{recursive:!0}),await Mm.writeFile(R,JSON.stringify(A))}catch(J){p.error("[plane-link] cursor flush failed:",J?.message||J)}}},xB),N.unref?.())}function g(J){return!M||M.readyState!==Nm.OPEN?!1:(M.send(JSON.stringify(J)),!0)}function h(J){let U=Date.now();for(let{trackId:E,event:P}of J){let we=O.get(P.id);O.set(P.id,{trackId:E,seq:P.seq,event:P,sentAt:U,tries:(we?.tries??0)+1})}return g({v:We,ch:"events",type:"append",events:J})}function k(J){let U=null;for(let E of O.values())E.trackId===J&&(U==null||E.seq<U)&&(U=E.seq);return U}function _(J){let U=V[J];if(U==null)return;let E=k(J),P=E==null?U:Math.min(U,E-1);P>(A[J]??0)&&(A[J]=P,be())}function he(){if(!T)return;let J=Date.now(),U=[...O.values()].filter(E=>E.event&&J-E.sentAt>=SB);if(U.length){U.sort((E,P)=>E.trackId===P.trackId?E.seq-P.seq:E.trackId<P.trackId?-1:1),p.log(`[plane-link] re-sending ${U.length} unacked event(s) the plane never confirmed`);for(let E=0;E<U.length;E+=Bl){let P=U.slice(E,E+Bl).map(we=>({trackId:we.trackId,event:we.event}));if(!h(P))return}}}async function ee(){if(!i)return;let J;try{J=await i()}catch(U){p.error("[plane-link] metrics sample failed:",U?.message||U);return}J&&g({v:We,ch:"ctl",type:"metrics",metrics:J})}function Te(){i&&(F&&clearInterval(F),ee(),F=setInterval(ee,c),F.unref?.())}async function je(){let J;try{J=await o()}catch(U){p.error("[plane-link] backfill listTracks failed:",U?.message||U);return}g({v:We,ch:"ctl",type:"tracks",ids:J.filter(U=>!U.transferredTo).map(U=>U.id).filter(U=>typeof U=="string")});for(let U of J){if(!T)return;let E;try{E=await B(U)}catch{continue}let P=A[U.id]??0,we=E.filter(ke=>ke.seq>P&&typeof ke.id=="string");for(let ke=0;ke<we.length;ke+=Bl){let ze=we.slice(ke,ke+Bl).map(mt=>({trackId:U.id,event:mt}));if(!h(ze))return}}}function Le(J){!T||!J?.trackId||typeof J.id!="string"||h([{trackId:J.trackId,event:J}])}let S=new Map;async function z(J){let{reqId:U,method:E,path:P,headers:we={},body:ke}=J,ze=Object.fromEntries(Object.entries(we).filter(([,Nt])=>Nt!=null)),mt=new AbortController;S.set(U,mt);try{if(J.host){await Sr(J,ze,mt);return}let Nt=await fetch(m+P,{method:E,headers:ze,body:ke?Buffer.from(ke,"base64"):void 0,signal:mt.signal}),$n=Object.fromEntries(Nt.headers);for(let ht of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete $n[ht];if($n["content-type"]||($n["content-type"]="application/octet-stream"),g({v:We,ch:"control",type:"http_head",reqId:U,status:Nt.status,headers:$n}),Nt.body){for await(let ht of Nt.body)if(!g({v:We,ch:"control",type:"http_data",reqId:U,data:Buffer.from(ht).toString("base64")}))break}g({v:We,ch:"control",type:"http_end",reqId:U,error:null})}finally{S.delete(U)}}function Sr(J,U,E){let{reqId:P,method:we,path:ke,body:ze}=J,mt=new URL(m);return new Promise(Nt=>{let $n=pB.request({host:mt.hostname,port:mt.port,method:we,path:ke,headers:{...U,host:J.host},signal:E.signal},ht=>{let{connection:_F,"keep-alive":IF,"transfer-encoding":RF,upgrade:$F,...NA}=ht.headers;g({v:We,ch:"control",type:"http_head",reqId:P,status:ht.statusCode||502,headers:NA}),ht.on("data",PA=>{g({v:We,ch:"control",type:"http_data",reqId:P,data:Buffer.from(PA).toString("base64")})||ht.destroy()}),ht.on("end",()=>{g({v:We,ch:"control",type:"http_end",reqId:P,error:null}),Nt()}),ht.on("error",()=>{g({v:We,ch:"control",type:"http_end",reqId:P,error:"view upstream error"}),Nt()})});$n.on("error",ht=>{g({v:We,ch:"control",type:"http_end",reqId:P,error:ht?.message||"view request failed"}),Nt()}),ze&&$n.write(Buffer.from(ze,"base64")),$n.end()})}function fo(){if(q)return;M=new Nm(e,{headers:{authorization:`Bearer ${t}`}}),M.on("open",async()=>{let U=null;try{U=await s?.()||null}catch{U=null}g({v:We,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:An,installKind:d,...typeof f=="function"?{updatePolicy:f()}:f?{updatePolicy:f}:{},providers:wB(),...U?{agents:U}:{}})}),M.on("message",U=>{let E;try{E=JSON.parse(U)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){T=!0,G=qv,ue=null,p.log(`[plane-link] connected to plane as ${n}`),E.minNodeVersion&&An&&ai(An,E.minNodeVersion)&&p.error(`[plane-link] this node is bostrat-node ${An}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${Wv(d)}`);try{u?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let P=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(P&&An&&ai(An,P)&&!y.has(P)){y.add(P),p.log(`[plane-link] bostrat-node ${P} is available (this node runs ${An}) \u2014 ${Wv(d)}`);try{l?.({current:An,latest:P,min:E.minNodeVersion??null,installKind:d,required:!!(E.minNodeVersion&&ai(An,E.minNodeVersion))})}catch(we){p.error(`[plane-link] update notice failed: ${we?.message||we}`)}}$&&clearInterval($),$=setInterval(()=>{g({v:We,ch:"ctl",type:"ping"}),he()},vB),$.unref?.(),Te(),je().catch(we=>p.error("[plane-link] backfill failed:",we?.message||we));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"){Mc(E.caps??{},{source:"plane",updatedAt:E.updatedAt??Date.now()}).then(P=>p.log(`[plane-link] spend caps ${Object.keys(P.caps).length?`updated (${Object.keys(P.caps).join(", ")})`:"cleared"} from plane`)).catch(P=>p.error("[plane-link] spend-caps push rejected:",P?.message||P));return}if(E.ch==="ctl"&&E.type==="transferred"&&Array.isArray(E.tracks)){Promise.resolve().then(()=>a?.(E.tracks)).catch(P=>p.error("[plane-link] transfer notice failed:",P?.message||P));return}if(E.ch==="events"&&E.type==="ack"&&Array.isArray(E.ids)){let P=new Set;for(let we of E.ids){let ke=O.get(we);ke&&(O.delete(we),(V[ke.trackId]??0)<ke.seq&&(V[ke.trackId]=ke.seq),P.add(ke.trackId))}for(let we of P)_(we);return}if(E.ch==="control"&&E.type==="http_open"){z(E).catch(P=>g({v:We,ch:"control",type:"http_end",reqId:E.reqId,error:P?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){S.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let P=new Nm(w+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);b.set(E.streamId,P),v.set(E.streamId,[]),P.on("open",()=>{let ke=v.get(E.streamId)||[];v.delete(E.streamId);for(let{buf:ze,bin:mt}of ke)P.send(ze,{binary:mt})}),P.on("message",(ke,ze)=>{let mt=ze?ke:Buffer.from(String(ke));g({v:We,ch:"attach",type:"data",streamId:E.streamId,data:mt.toString("base64"),...ze?{bin:!0}:{}})});let we=()=>{v.delete(E.streamId),b.delete(E.streamId)&&g({v:We,ch:"attach",type:"close",streamId:E.streamId})};P.on("close",we),P.on("error",we);return}if(E.ch==="attach"&&E.type==="data"){let P=b.get(E.streamId);if(!P)return;let we=Buffer.from(E.data,"base64"),ke=E.bin===!0,ze=v.get(E.streamId);ze?ze.push({buf:we,bin:ke}):P.send(we,{binary:ke});return}if(E.ch==="attach"&&E.type==="close"){let P=b.get(E.streamId);b.delete(E.streamId),v.delete(E.streamId),P?.close();return}});let J=()=>{T&&p.log("[plane-link] disconnected from plane"),T=!1,$&&(clearInterval($),$=null),F&&(clearInterval(F),F=null),O.clear(),V={};for(let P of b.values())P.close();b.clear(),v.clear();for(let P of S.values())P.abort();if(S.clear(),q||Q)return;ue==null&&(ue=Date.now());let U=Date.now()-ue<yB,E=U?bB+Math.random()*kB:G;Q=setTimeout(()=>{Q=null,fo()},E),Q.unref?.(),U||(G=Math.min(G*2,gB))};M.on("close",J),M.on("error",U=>{let E=/Unexpected server response: (\d+)/.exec(U?.message||"")?.[1];(E==="401"||E==="403")&&Date.now()-Re>=AB&&(Re=Date.now(),p.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)`)),J()})}return ot.on("event",Le),De().then(fo),{stop:()=>{q=!0,ot.off("event",Le),Q&&clearTimeout(Q),N&&clearTimeout(N),$&&clearInterval($),F&&clearInterval(F),g({v:We,ch:"ctl",type:"bye",reason:"shutdown"}),M?.close()},status:()=>({connected:T,nodeId:n,cursor:{...A},inflight:O.size})}}lc();var jl=(()=>{try{return IB(import.meta.url)("../package.json").version||null}catch{return null}})(),RB=process.env.BOSTRAT_STATE_DIR||Ke.join(process.env.HOME||EB.tmpdir(),".bostrat"),zv=Ke.join(RB,"self-update"),Ll=Ke.join(zv,"status.json"),$B=1440*6e4,OB=6e4,Pm=new Set(["npm-global","git-checkout"]);function Jv({kind:e=oo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Pm.has(e)?e==="git-checkout"||CB(t)?"auto":"prompt":"off"}function CB(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var $t=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function MB({kind:e,to:t,from:n=jl,statusFile:r=Ll,logFile:o=Ke.join(zv,"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||Ke.resolve(Ke.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Pm.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let f=(y,b="")=>`write_status ${$t(y)} ${b}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${$t(i)}`,m=`
|
|
488
|
+
`);mD=new Set(["claude","codex","cursor"]);oi=new Set,hD=e=>oi.has(e);wD=9e4;IS={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};em=new Map;am=120;Cm=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:js(e.hookToken),createdAt:Date.now()});ys=new Map,xs=e=>ys.get(e)||(At?re(At,e)?.handoff:null)||null;im=6e4,Cn=(e,t,n=15e3)=>OS("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),cm=new Map,Nm=e=>cm.get(e)||null;tm=new Map;vD=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,TD=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,bs=G.join(rn,"worktrees"),ps=[Qa,_l],wt=new Map,AD=e=>wt.get(e)||(At?At.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,co=e=>{let t=new Map([...wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=At?re(At,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},Ul=e=>({id:e.id,trackId:e.trackId,trackName:e.trackName,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,reason:e.reason||null,step:e.step,failedStep:e.failedStep||null,box:e.box,branch:e.branch,base:e.base,repos:e.repos||null,dir:e.dir,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt});mi=e=>QS(e.trackId,e.id,Ul(e));Fm=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",ai=class extends Error{}});tt();import we from"node:path";import Ye from"node:fs";import LF from"node:readline";import{fileURLToPath as FF}from"node:url";import It from"express";import UF from"node:http";import HF from"node:https";import Yh from"node:os";import{WebSocketServer as WF}from"ws";tt();import fI from"node-pty";async function cg(e,t,n={}){if(!_e(t)){wc(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Vn()){wc(e,`host tmux server not running on ${ft}`),e.close(1011,"tmux server unavailable");return}if(!await se(t)){wc(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=ig(n.cols,120,lg),o=ig(n.rows,32,ug),s;try{s=fI.spawn(U,["-S",ft,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){wc(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&od(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(od(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"&&wI(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{}hI(t)})}var pI=6,mI=250;async function hI(e,t=pI,n=mI){for(let r=0;r<t;r++){if(await Nr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var lg=20,ug=6;function wI(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=lg&&Number(t)>=ug}function ig(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function od(e,t){try{e.send(JSON.stringify(t))}catch{}}function wc(e,t){od(e,{type:"error",message:t})}function dg(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}}Mt();yc();zd();fr();import{createRequire as qF}from"node:module";var PD=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function qm({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(PD)&&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 dv from"node:fs";import yr from"node:fs/promises";import YD from"node:os";import Ve from"node:path";import{spawn as XD}from"node:child_process";import{createRequire as ZD}from"node:module";ue();Hc();import lv from"node:fs";import Gm from"node:fs/promises";import jD from"node:http";import Nn from"node:path";import Kl from"node:os";import{createRequire as LD}from"node:module";import Vm from"ws";import DD from"node:path";import BD from"node:os";var hi=process.env.BOSTRAT_STATE_DIR||DD.join(process.env.HOME||BD.tmpdir(),".bostrat");function Km(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 sv(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Km(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 Bt=(()=>{try{return LD(import.meta.url)("../package.json").version||null}catch{return null}})(),FD=process.env.BOSTRAT_STATE_DIR||Nn.join(process.env.HOME||Kl.tmpdir(),".bostrat");function lo({env:e=process.env,pkgDir:t=Nn.resolve(Nn.dirname(new URL(import.meta.url).pathname),".."),exists:n=lv.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(Nn.join(t,"..",".git"))?"git-checkout":"dev"}function wi(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function Wl(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 UD({home:e=process.env.HOME||Kl.homedir(),env:t=process.env}={}){let n=r=>{try{return lv.statSync(r),!0}catch{return!1}};return{claude:n(Nn.join(e,".claude",".credentials.json")),codex:n(Nn.join(t.CODEX_HOME||Nn.join(e,".codex"),"auth.json"))}}var Ke=1,iv=1e3,cv=3e4,HD=6e4,WD=1500,qD=500,ql=100,KD=2e3,GD=25e3,VD=6e4,zD=6e4,JD=6e4;function uv({url:e,token:t,nodeId:n=Kl.hostname(),name:r=Kl.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=zD,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:f=null,updatePolicy:d=null,installKind:p=lo(),selfUrl:m=`http://127.0.0.1:${process.env.PORT||3362}`,log:h=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=av(e);let g=m.replace(/^http/,"ws"),x=new Set,k=new Map,A=new Map,R=Nn.join(FD,"plane-link","cursor.json"),M={},N={},C=new Map,T=null,j=!1,Z=!1,fe=iv,ie=null,D=null,I=null,F=null,pe=null,Se=!1,He=0;async function le(){try{M=JSON.parse(await Gm.readFile(R,"utf8"))}catch{M={}}}function W(){Se=!0,!I&&(I=setTimeout(async()=>{if(I=null,!!Se){Se=!1;try{await Gm.mkdir(Nn.dirname(R),{recursive:!0}),await Gm.writeFile(R,JSON.stringify(M))}catch(te){h.error("[plane-link] cursor flush failed:",te?.message||te)}}},KD),I.unref?.())}function J(te){return!T||T.readyState!==Vm.OPEN?!1:(T.send(JSON.stringify(te)),!0)}function ee(te){let V=Date.now();for(let{trackId:E,event:B}of te){let ge=C.get(B.id);C.set(B.id,{trackId:E,seq:B.seq,event:B,sentAt:V,tries:(ge?.tries??0)+1})}return J({v:Ke,ch:"events",type:"append",events:te})}function Xe(te){let V=null;for(let E of C.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=Xe(te),B=E==null?V:Math.min(V,E-1);B>(M[te]??0)&&(M[te]=B,W())}function w(){if(!j)return;let te=Date.now(),V=[...C.values()].filter(E=>E.event&&te-E.sentAt>=VD);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(ge=>({trackId:ge.trackId,event:ge.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,ge=E.filter(ye=>ye.seq>B&&typeof ye.id=="string");for(let ye=0;ye<ge.length;ye+=ql){let Ze=ge.slice(ye,ye+ql).map(bt=>({trackId:V.id,event:bt}));if(!ee(Ze))return}}}function b(te){!j||!te?.trackId||typeof te.id!="string"||ee([{trackId:te.trackId,event:te}])}let P=new Map;async function Ce(te){let{reqId:V,method:E,path:B,headers:ge={},body:ye}=te,Ze=Object.fromEntries(Object.entries(ge).filter(([,Wt])=>Wt!=null)),bt=new AbortController;P.set(V,bt);try{if(te.host){await We(te,Ze,bt);return}let Wt=await fetch(m+B,{method:E,headers:Ze,body:ye?Buffer.from(ye,"base64"):void 0,signal:bt.signal}),jn=Object.fromEntries(Wt.headers);for(let kt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete jn[kt];if(jn["content-type"]||(jn["content-type"]="application/octet-stream"),J({v:Ke,ch:"control",type:"http_head",reqId:V,status:Wt.status,headers:jn}),Wt.body){for await(let kt of Wt.body)if(!J({v:Ke,ch:"control",type:"http_data",reqId:V,data:Buffer.from(kt).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:ge,path:ye,body:Ze}=te,bt=new URL(m);return new Promise(Wt=>{let jn=jD.request({host:bt.hostname,port:bt.port,method:ge,path:ye,headers:{...V,host:te.host},signal:E.signal},kt=>{let{connection:iU,"keep-alive":cU,"transfer-encoding":lU,upgrade:uU,...sE}=kt.headers;J({v:Ke,ch:"control",type:"http_head",reqId:B,status:kt.statusCode||502,headers:sE}),kt.on("data",aE=>{J({v:Ke,ch:"control",type:"http_data",reqId:B,data:Buffer.from(aE).toString("base64")})||kt.destroy()}),kt.on("end",()=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:null}),Wt()}),kt.on("error",()=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),Wt()})});jn.on("error",kt=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:kt?.message||"view request failed"}),Wt()}),Ze&&jn.write(Buffer.from(Ze,"base64")),jn.end()})}function Ht(){if(Z)return;T=new Vm(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:Bt,installKind:p,...typeof d=="function"?{updatePolicy:d()}:d?{updatePolicy:d}:{},providers:UD(),...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=iv,ie=null,h.log(`[plane-link] connected to plane as ${n}`),E.minNodeVersion&&Bt&&wi(Bt,E.minNodeVersion)&&h.error(`[plane-link] this node is bostrat-node ${Bt}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${Wl(p)}`);try{u?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let B=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(B&&Bt&&wi(Bt,B)&&!x.has(B)){x.add(B),h.log(`[plane-link] bostrat-node ${B} is available (this node runs ${Bt}) \u2014 ${Wl(p)}`);try{l?.({current:Bt,latest:B,min:E.minNodeVersion??null,installKind:p,required:!!(E.minNodeVersion&&wi(Bt,E.minNodeVersion))})}catch(ge){h.error(`[plane-link] update notice failed: ${ge?.message||ge}`)}}F&&clearInterval(F),F=setInterval(()=>{J({v:Ke,ch:"ctl",type:"ping"}),w()},GD),F.unref?.(),$(),ve().catch(ge=>h.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;h.error(`[plane-link] plane REFUSED this node: bostrat-node ${Bt??"?"} is below its minimum ${B??"?"} \u2014 ${E.fix||Wl(p)}`),fe=cv,ie=0;try{f?.({current:Bt,min:B,latest:ge,fix:E.fix||Wl(p),installKind:p})}catch(ye){h.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"){Uc(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 ge of E.ids){let ye=C.get(ge);ye&&(C.delete(ge),(N[ye.trackId]??0)<ye.seq&&(N[ye.trackId]=ye.seq),B.add(ye.trackId))}for(let ge of B)y(ge);return}if(E.ch==="control"&&E.type==="http_open"){Ce(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 Vm(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 ye=A.get(E.streamId)||[];A.delete(E.streamId);for(let{buf:Ze,bin:bt}of ye)B.send(Ze,{binary:bt})}),B.on("message",(ye,Ze)=>{let bt=Ze?ye:Buffer.from(String(ye));J({v:Ke,ch:"attach",type:"data",streamId:E.streamId,data:bt.toString("base64"),...Ze?{bin:!0}:{}})});let ge=()=>{A.delete(E.streamId),k.delete(E.streamId)&&J({v:Ke,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,Ze=A.get(E.streamId);Ze?Ze.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),A.delete(E.streamId),B?.close();return}});let te=()=>{j&&h.log("[plane-link] disconnected from plane"),j=!1,F&&(clearInterval(F),F=null),pe&&(clearInterval(pe),pe=null),C.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<HD,E=V?WD+Math.random()*qD:fe;D=setTimeout(()=>{D=null,Ht()},E),D.unref?.(),V||(fe=Math.min(fe*2,cv))};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>=JD&&(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 st.on("event",b),le().then(Ht),{stop:()=>{Z=!0,st.off("event",b),D&&clearTimeout(D),I&&clearTimeout(I),F&&clearInterval(F),pe&&clearInterval(pe),J({v:Ke,ch:"ctl",type:"bye",reason:"shutdown"}),T?.close()},status:()=>({connected:j,nodeId:n,cursor:{...M},inflight:C.size})}}bc();var Gl=(()=>{try{return ZD(import.meta.url)("../package.json").version||null}catch{return null}})(),QD=process.env.BOSTRAT_STATE_DIR||Ve.join(process.env.HOME||YD.tmpdir(),".bostrat"),fv=Ve.join(QD,"self-update"),Vl=Ve.join(fv,"status.json"),eB=1440*6e4,tB=6e4,zm=new Set(["npm-global","git-checkout"]);function pv({kind:e=lo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:zm.has(e)?e==="git-checkout"||nB(t)?"auto":"prompt":"off"}function nB(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var jt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function rB({kind:e,to:t,from:n=Gl,statusFile:r=Vl,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(!zm.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let f=(g,x="")=>`write_status ${jt(g)} ${x}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${jt(i)}`,p=`
|
|
487
489
|
health() {
|
|
488
490
|
for i in $(seq 1 45); do
|
|
489
491
|
sleep 1
|
|
490
492
|
if curl -sf -o /dev/null -I "http://127.0.0.1:${s}/"; then return 0; fi
|
|
491
493
|
done
|
|
492
494
|
return 1
|
|
493
|
-
}`,
|
|
495
|
+
}`,m=`#!/usr/bin/env bash
|
|
494
496
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
495
497
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
496
498
|
set -u
|
|
497
|
-
FROM=${
|
|
498
|
-
TO=${
|
|
499
|
-
STATUS_FILE=${
|
|
499
|
+
FROM=${jt(n??"")}
|
|
500
|
+
TO=${jt(t)}
|
|
501
|
+
STATUS_FILE=${jt(r)}
|
|
500
502
|
STARTED_AT=${Number(u)}
|
|
501
503
|
write_status() { # <status> [error]
|
|
502
504
|
local err="\${2:-}"
|
|
503
505
|
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
504
506
|
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
505
|
-
"$1" "$FROM" "$TO" ${
|
|
507
|
+
"$1" "$FROM" "$TO" ${jt(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
506
508
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
507
509
|
}
|
|
508
|
-
${
|
|
509
|
-
`;if(e==="npm-global"){let
|
|
510
|
+
${p}
|
|
511
|
+
`;if(e==="npm-global"){let g=a?Ve.join(a,"bin","npm"):"npm",x=k=>`${jt(g)} install -g "bostrat-node@${k}" --no-audit --no-fund`;return m+`
|
|
510
512
|
${f("updating",'"" ')}
|
|
511
|
-
if ! ${
|
|
513
|
+
if ! ${x("$TO")}; then
|
|
512
514
|
${f("failed",'"npm install failed"')}
|
|
513
515
|
exit 1
|
|
514
516
|
fi
|
|
@@ -523,84 +525,84 @@ if health; then
|
|
|
523
525
|
fi
|
|
524
526
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
525
527
|
${f("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
526
|
-
if [ -n "$FROM" ] && ${
|
|
528
|
+
if [ -n "$FROM" ] && ${x("$FROM")} && ${d} && health; then
|
|
527
529
|
${f("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
528
530
|
exit 1
|
|
529
531
|
fi
|
|
530
532
|
${f("failed",'"agent did not answer after the update AND the rollback"')}
|
|
531
533
|
exit 1
|
|
532
|
-
`}let
|
|
533
|
-
LIVE=${
|
|
534
|
+
`}let h=g=>`( cd ${jt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return m+`
|
|
535
|
+
LIVE=${jt(l)}
|
|
534
536
|
TMP_SCRIPT="$(mktemp)"
|
|
535
537
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
536
538
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
537
539
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
538
540
|
# and nothing here may write after that boot or the settle would repeat.
|
|
539
541
|
${f("restarting")}
|
|
540
|
-
if ${
|
|
542
|
+
if ${h("$TO")}; then
|
|
541
543
|
exit 0
|
|
542
544
|
fi
|
|
543
545
|
${f("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
544
|
-
if [ -n "$FROM" ] && ${
|
|
546
|
+
if [ -n "$FROM" ] && ${h("$FROM")}; then
|
|
545
547
|
${f("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
546
548
|
exit 1
|
|
547
549
|
fi
|
|
548
550
|
${f("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
549
551
|
exit 1
|
|
550
|
-
`}async function ks(e=Ll){try{return JSON.parse(await dr.readFile(e,"utf8"))}catch{return null}}var NB=new Set(["updating","restarting","rolling-back"]),ii=e=>!!(e&&NB.has(e.status));async function Bm({to:e,from:t=jl,kind:n=oo(),reason:r="manual",statusFile:o=Ll,spawnImpl:s=_B,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Pm.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!ai(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await ks(o);if(ii(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 dr.mkdir(Ke.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?dd(await PB())||Ke.resolve(Ke.dirname(process.execPath),".."):null,f=MB({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=Ke.join(Ke.dirname(o),`update-${e}.sh`),m=Ke.join(Ke.dirname(o),"last.log");await dr.writeFile(d,f,{mode:448}),await dr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let p=Vv.existsSync("/run/systemd/system")&&await BB("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${$t(d)} >> ${$t(m)} 2>&1`]];try{s(p[0],p[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await dr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${w?.message||w}`})),{ok:!1,reason:`could not spawn updater: ${w?.message||w}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:d,statusFile:o}}async function PB(){try{return await dr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function BB(e){for(let t of String(process.env.PATH||"").split(Ke.delimiter))try{return await dr.access(Ke.join(t,e),Vv.constants.X_OK),!0}catch{}return!1}var DB=45*6e4;async function Yv({statusFile:e=Ll,current:t=jl,now:n=Date.now,staleMs:r=DB}={}){let o=await ks(e);if(!o)return null;let s=a=>dr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(ii(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 Xv({isBusy:e,start:t=Bm,onStuck:n=null,onStarted:r=null,tickMs:o=OB,stuckMs:s=$B,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,f=()=>{u?.timer&&c(u.timer),u=null},d=async()=>{if(!u)return;let m=!0;try{m=await e()!==!1}catch{m=!0}if(m){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 p=u;f();let w=await t({to:p.to,from:p.from,kind:p.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...p,result:w})}catch{}};return{arm({to:m,from:p=jl,kind:w=oo()}){return u?.to===m?!1:(f(),u={to:m,from:p,kind:w,since:a(),stuckFired:!1,timer:i(d,o)},u.timer.unref?.(),!0)},tick:d,cancel:f,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}le();Je();var jB=(...e)=>fetch(...e);function LB(e=process.env){return!!(ge(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Dm(e=process.env){if(!LB(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 jB(`${ge(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 xs(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Dm(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 Fl(e,t,{source:n="chat"}={}){return I(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}le();Je();var FB=(...e)=>fetch(...e);async function Ul(e,t,n,r=process.env){if(!(ge(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 FB(`${ge(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 UB(e,t=process.env){return Ul("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function Zv({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await UB(t,o):await xs(e,o),a=await Ul("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function jm(e,t=process.env){let n=await xs(e,t),r=await Ul("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Lm(e,t=process.env){return(await Ul("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function Qv(e,t,n,r=process.env){let{blueprint:o,items:s}=await jm(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await I(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}le();Je();var HB=(...e)=>fetch(...e);async function WB(e,t,n,r=process.env){if(!(ge(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 HB(`${ge(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}async function eS(e,{title:t,context:n="",decision:r,alternatives:o=[],supersedesId:s=null}){if(!String(t??"").trim())throw new Error("a title is required");if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await I(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:String(t).trim(),context:String(n??"").trim(),decision:String(r).trim(),alternatives:(Array.isArray(o)?o:[]).map(a=>typeof a=="string"?{label:a}:a).filter(a=>a?.label),...s?{supersedesId:s}:{}}}),{title:String(t).trim()}}function tS(e,t=process.env){return WB("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Cp();le();import pi from"node:fs/promises";import Yl from"node:path";import MD from"node:os";Nn();import qB from"node:fs/promises";import nS from"node:path";import KB from"node:os";var rS=()=>nS.join(process.env.BOSTRAT_STATE_DIR||nS.join(process.env.HOME||KB.tmpdir(),".bostrat"),"integrations.json");async function _n(){try{return JSON.parse(await qB.readFile(rS(),"utf8"))||{}}catch{return{}}}var Hl=["linked","prOpened","prMerged","create"],Fm=["prompt","auto","off"],GB={create:["prompt","off"]},oS=e=>GB[e]??Fm;function Wl(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Hl){let s=oS(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 so(){let e=await _n(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:Wl(e),defaultTeam:n}:null}async function ql(){let e=await _n(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:Wl(e)}}async function Um(){return Wl(await _n())}async function sS(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Hl.join(", ")}`);for(let[r,o]of t){if(!Hl.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Hl.join(", ")}`);let s=oS(r);if(!s.includes(o))throw new Error(s.length<Fm.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Fm.join(", ")}`)}let n=await _n();return n.ticketFlow={...Wl(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await nt(rS(),n,{mode:384}),n.ticketFlow}async function ci(){let e=(await _n()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function VB(){let e=(await _n()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var zB={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Kl(){let t=(await _n()).voiceChat||{},n=t.provider||"openai",r=zB[n];if(!r)return null;let o=n==="openai"?await VB():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function aS(){let e=(await _n()).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 ao(){let e=(await _n()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}le();Bn();Pr();cs();import ui from"node:fs/promises";import hS from"node:path";import bD from"node:os";import{randomUUID as wS}from"node:crypto";var Wm={};ku(Wm,{createComment:()=>rD,findIssueUrl:()=>ZB,issueStates:()=>aD,mdToAdf:()=>lS,moveIssue:()=>iD,parseIssueRef:()=>XB,resolveIssue:()=>QB,uploadImage:()=>oD,upsertCard:()=>eD,whoami:()=>Hm});var vs=e=>String(e.baseUrl||"").replace(/\/$/,""),cS=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function io(e,t,n,r){let o=await fetch(`${vs(e)}${n}`,{method:t,headers:{authorization:cS(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 JB=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,YB=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function XB(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(JB);return r?r[1].toUpperCase():null}let n=t.match(YB);return n?n[1]:null}function ZB(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 QB(e,t){let n;try{n=await io(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:`${vs(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function eD(e,t,{url:n,title:r,subtitle:o}){await io(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 tD=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function nD(e){let t=[],n=0;for(let r of e.matchAll(tD)){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 lS(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
551
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
552
|
+
`}async function Ss(e=Vl){try{return JSON.parse(await yr.readFile(e,"utf8"))}catch{return null}}var oB=new Set(["updating","restarting","rolling-back"]),gi=e=>!!(e&&oB.has(e.status));async function zl({to:e,from:t=Gl,kind:n=lo(),reason:r="manual",statusFile:o=Vl,spawnImpl:s=XD,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!zm.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!wi(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await Ss(o);if(gi(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await yr.mkdir(Ve.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?xd(await sB())||Ve.resolve(Ve.dirname(process.execPath),".."):null,f=rB({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=Ve.join(Ve.dirname(o),`update-${e}.sh`),p=Ve.join(Ve.dirname(o),"last.log");await yr.writeFile(d,f,{mode:448}),await yr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let m=dv.existsSync("/run/systemd/system")&&await aB("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${jt(d)} >> ${jt(p)} 2>&1`]];try{s(m[0],m[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(h){return await yr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${h?.message||h}`})),{ok:!1,reason:`could not spawn updater: ${h?.message||h}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:d,statusFile:o}}async function sB(){try{return await yr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function aB(e){for(let t of String(process.env.PATH||"").split(Ve.delimiter))try{return await yr.access(Ve.join(t,e),dv.constants.X_OK),!0}catch{}return!1}var iB=45*6e4;async function mv({statusFile:e=Vl,current:t=Gl,now:n=Date.now,staleMs:r=iB}={}){let o=await Ss(e);if(!o)return null;let s=a=>yr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(gi(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function hv({isBusy:e,start:t=zl,onStuck:n=null,onStarted:r=null,tickMs:o=tB,stuckMs:s=eB,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,f=()=>{u?.timer&&c(u.timer),u=null},d=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let m=u;f();let h=await t({to:m.to,from:m.from,kind:m.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${h.ok?"started":`not started: ${h.reason}`}`);try{await r?.({...m,result:h})}catch{}};return{arm({to:p,from:m=Gl,kind:h=lo()}){return u?.to===p?!1:(f(),u={to:p,from:m,kind:h,since:a(),stuckFired:!1,timer:i(d,o)},u.timer.unref?.(),!0)},tick:d,cancel:f,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ue();Qe();var cB=(...e)=>fetch(...e);function lB(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Jm(e=process.env){if(!lB(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 cB(`${ke(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function vs(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Jm(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 Jl(e,t,{source:n="chat"}={}){return _(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ue();Qe();var uB=(...e)=>fetch(...e);async function Yl(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 uB(`${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 dB(e,t=process.env){return Yl("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function wv({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await dB(t,o):await vs(e,o),a=await Yl("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Ym(e,t=process.env){let n=await vs(e,t),r=await Yl("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Xm(e,t=process.env){return(await Yl("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function gv(e,t,n,r=process.env){let{blueprint:o,items:s}=await Ym(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await _(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ue();Qe();var fB=(...e)=>fetch(...e);async function pB(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 fB(`${ke(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}async function yv(e,{title:t,context:n="",decision:r,alternatives:o=[],supersedesId:s=null}){if(!String(t??"").trim())throw new Error("a title is required");if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await _(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:String(t).trim(),context:String(n??"").trim(),decision:String(r).trim(),alternatives:(Array.isArray(o)?o:[]).map(a=>typeof a=="string"?{label:a}:a).filter(a=>a?.label),...s?{supersedesId:s}:{}}}),{title:String(t).trim()}}function bv(e,t=process.env){return pB("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Up();ue();import vi from"node:fs/promises";import su from"node:path";import r1 from"node:os";Wn();import mB from"node:fs/promises";import kv from"node:path";import hB from"node:os";var xv=()=>kv.join(process.env.BOSTRAT_STATE_DIR||kv.join(process.env.HOME||hB.tmpdir(),".bostrat"),"integrations.json");async function Pn(){try{return JSON.parse(await mB.readFile(xv(),"utf8"))||{}}catch{return{}}}var Xl=["linked","prOpened","prMerged","create"],Zm=["prompt","auto","off"],wB={create:["prompt","off"]},Sv=e=>wB[e]??Zm;function Zl(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Xl){let s=Sv(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function uo(){let e=await Pn(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:Zl(e),defaultTeam:n}:null}async function Ql(){let e=await Pn(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:Zl(e)}}async function Qm(){return Zl(await Pn())}async function vv(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Xl.join(", ")}`);for(let[r,o]of t){if(!Xl.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Xl.join(", ")}`);let s=Sv(r);if(!s.includes(o))throw new Error(s.length<Zm.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Zm.join(", ")}`)}let n=await Pn();return n.ticketFlow={...Zl(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await at(xv(),n,{mode:384}),n.ticketFlow}async function yi(){let e=(await Pn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function gB(){let e=(await Pn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var yB={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function eu(){let t=(await Pn()).voiceChat||{},n=t.provider||"openai",r=yB[n];if(!r)return null;let o=n==="openai"?await gB():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function Tv(){let e=(await Pn()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function fo(){let e=(await Pn()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}ue();Kn();Fr();us();import ki from"node:fs/promises";import Mv from"node:path";import WB from"node:os";import{randomUUID as Nv}from"node:crypto";var th={};Ru(th,{createComment:()=>_B,findIssueUrl:()=>SB,issueStates:()=>$B,mdToAdf:()=>_v,moveIssue:()=>OB,parseIssueRef:()=>xB,resolveIssue:()=>vB,uploadImage:()=>IB,upsertCard:()=>TB,whoami:()=>eh});var Ts=e=>String(e.baseUrl||"").replace(/\/$/,""),Ev=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function po(e,t,n,r){let o=await fetch(`${Ts(e)}${n}`,{method:t,headers:{authorization:Ev(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 bB=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,kB=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function xB(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(bB);return r?r[1].toUpperCase():null}let n=t.match(kB);return n?n[1]:null}function SB(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 vB(e,t){let n;try{n=await po(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Ts(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function TB(e,t,{url:n,title:r,subtitle:o}){await po(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var AB=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function EB(e){let t=[],n=0;for(let r of e.matchAll(AB)){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 _v(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
553
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...EB(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 _B(e,t,n){let r=await po(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:_v(n)});return r?.id?`${Ts(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function eh(e){let t=await po(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Ts(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function IB(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(`${Ts(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:Ev(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??`${Ts(e)}/rest/api/3/attachment/content/${c.id}`}var RB={new:"unstarted",indeterminate:"started",done:"completed"},Av=e=>RB[e?.key]??"started";async function $B(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([po(e,"GET",`/rest/api/3/issue/${n}?fields=status`),po(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Av(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Av(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function OB(e,t,n){await po(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var sh={};Ru(sh,{createComment:()=>LB,createIssue:()=>oh,createProject:()=>rh,findIssueUrl:()=>DB,issueStates:()=>UB,moveIssue:()=>HB,parseIssueRef:()=>PB,resolveIssue:()=>BB,teams:()=>As,uploadImage:()=>FB,upsertCard:()=>jB,whoami:()=>nh});var CB=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function sn(e,t,n={}){let r=await fetch(CB(),{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 MB=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,NB=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function PB(e){let t=String(e||"").trim(),n=t.match(MB);if(n)return n[1].toUpperCase();let r=t.match(NB);return r?r[1]:null}function DB(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 BB(e,t){let r=(await sn(e,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:t})).issue;if(!r)throw new Error(`linear: issue "${t}" not found`);return{issueId:r.id,key:r.identifier,title:r.title,url:r.url,state:r.state?.name??null,team:r.team?.key??null}}async function jB(e,t,{url:n,title:r,subtitle:o}){let s=await sn(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 LB(e,t,n){let r=await sn(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 nh(e){let t=await sn(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 FB(e,{filename:t,contentType:n,buffer:r}){let o=await sn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
552
554
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
553
555
|
success
|
|
554
556
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
555
557
|
}
|
|
556
|
-
}`,{contentType:n,filename:t,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function gD(e,t){let n=await Xt(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 yD(e,t,n){if(!(await Xt(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 Ss(e){return((await Xt(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var uS=255;async function Km(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>uS,a=s?`${o.slice(0,uS-1).trimEnd()}\u2026`:o,i=await Xt(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 Gm(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Xt(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 gS={linear:{api:Vm,config:so},jira:{api:Wm,config:ql}};async function eh(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(gS)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function co(e){let t=gS[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function th(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 yS=()=>process.env.BOSTRAT_STATE_DIR||hS.join(process.env.HOME||bD.tmpdir(),".bostrat"),Xm=()=>hS.join(yS(),"tickets.json"),bS=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Gl=e=>`${bS()}/track/${e}`,fr=null,zm=null,Jm=Promise.resolve();async function fi(){return fr||(zm??=ui.readFile(Xm(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(fr=e,!t&&ve()){let{records:n,source:r}=await Pe("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;fr[s]=i}Object.keys(fr).length&&await Zm().catch(()=>{})}}return fr}).finally(()=>zm=null),zm)}async function Zm(){let e=JSON.stringify({tickets:fr},null,2),t=`${Xm()}.${process.pid}.tmp`;await ui.mkdir(yS(),{recursive:!0}),await ui.writeFile(t,e),await ui.rename(t,Xm())}async function pr(e=null){if(Jm=Jm.then(Zm,Zm),await Jm.catch(()=>{}),!e)return;let t=fr?.[e];t?Ye("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):St("tickets",e).catch(()=>{})}async function Zt(e){return(await fi())[e]??null}async function kS(){return{...await fi()}}async function xS(){return(await eh()).length>0}async function Ts(e,t,{source:n="api"}={}){let r=await eh();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let f=u.api.parseIssueRef(t);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await fi())[e.id]=c,await pr(e.id),await I({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await di(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 li(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await TS(e.id,c,"linked"),c}async function nh(e){let t=await fi();return t[e]?(delete t[e],await pr(e),!0):!1}var Qm=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function vS(e,t){if(!e.track?.cwd)return null;let n=Vn(await B({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function kD(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Qm(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Qm(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function xD(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(Qm(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function di(e,t){let n=await co(t);if(!n)return;let r=await vS(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Gl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:xD(t,r)})}function Vl(e){let t=new URL(bS());return`${t.protocol}//${e}.${t.host}/`}var vD={linked:"linked",opened:"prOpened",merged:"prMerged"};function SD(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 SS(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await pr(e),await I({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function TS(e,t,n){let r=await co(t);if(!r)return;let o=r.cfg.ticketFlow?.[vD[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=th(s,a),c=SD(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 SS(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await I({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:wS(),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 AS(e){let t=await Zt(e);if(!t)throw new Error("no ticket linked to this Track");let n=await co(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=th(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await pr(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 rh(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Zt(e);if(!r)throw new Error("no ticket linked to this Track");let o=await co(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=th(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 pr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await SS(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function ES(e,t){let n=await Zt(e);if(!n)throw new Error("no ticket linked to this Track");let r=await co(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await I({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function _S(e,t,n){if(!t)throw new Error("offerId required");let r=await B({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await rh(e.id,o.payload.to?.id,{mode:"offer"})),await I({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var dS=50;async function IS(){let e=await so();if(!e)throw new Error("no ticketing integration configured");return Ss(e)}function TD(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>dS)throw new Error(`too many issues (${n.length}); cap is ${dS} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function AD(e,t,n,r){let o=r??await Ss(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 Zt(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 RS(e,t){let n=await so();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=TD(t),o=await Ss(n),s=await AD(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=wS();return await I({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function $S(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await B({id:e.id,cwd:e.cwd}),s=o.find(p=>p.type==="ticket.create.offered"&&p.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(p=>p.type==="ticket.create.answered"&&p.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await I({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await so();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let p=(await Ss(a)).find(w=>w.id===r);if(!p)throw new Error("unknown team");c={id:p.id,key:p.key,name:p.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let p=await Km(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=p.id,l.push({kind:"epic",title:p.name,url:p.url})}for(let p of i.issues||[])try{let w=await Gm(a,{teamId:c.id,title:p.title,description:p.description,projectId:f,priority:p.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:p.title,message:w?.message||String(w)})}await I({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await I({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(p=>p.kind==="issue"),m=null;return i.linkCurrentTrack&&d.length===1&&!await Zt(e.id)&&(m=await Ts(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:m?m.key:null}}async function li(e,t){let n=await co(e);n&&await n.api.createComment(n.cfg,e,t)}async function ED(e,t,n){let r=await co(e);if(!r)return null;let o=await gn(t);if(!o)return null;let s=Rt(o,n);if(!s)return null;let a=await ui.readFile(bt(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 fS=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Ym=new Map;function _D(e,t){if(fS()<=0)return di(e,t);if(Ym.has(e))return;let n=setTimeout(()=>{Ym.delete(e),Zt(e).then(r=>r?di(e,r):null).catch(()=>{})},fS());n.unref?.(),Ym.set(e,n)}var pS=new Map;async function mS(e,t,n){let r=pS.get(e)??0;Date.now()-r<600*1e3||(pS.set(e,Date.now()),await I({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function ID(e,{resolveTrack:t=null}={}){let n=await Zt(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await eh())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 Ts(s,o,{source:"chat"})}catch(a){await mS(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 pr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await pr(e.trackId),await di(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 pr(e.trackId),await di(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await vS(n,e.trackId).catch(()=>null):null;await li(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
558
|
+
}`,{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 UB(e,t){let n=await sn(e,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:t.key});if(!n.issue)throw new Error(`linear: issue "${t.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function HB(e,t,n){if(!(await sn(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function As(e){return((await sn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Iv=255;async function rh(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>Iv,a=s?`${o.slice(0,Iv-1).trimEnd()}\u2026`:o,i=await sn(e,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[t],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function oh(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await sn(e,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:t,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var Pv={linear:{api:sh,config:uo},jira:{api:th,config:Ql}};async function fh(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(Pv)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function mo(e){let t=Pv[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function ph(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 Dv=()=>process.env.BOSTRAT_STATE_DIR||Mv.join(process.env.HOME||WB.tmpdir(),".bostrat"),lh=()=>Mv.join(Dv(),"tickets.json"),Bv=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),tu=e=>`${Bv()}/track/${e}`,br=null,ah=null,ih=Promise.resolve();async function Si(){return br||(ah??=ki.readFile(lh(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(br=e,!t&&Ae()){let{records:n,source:r}=await je("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;br[s]=i}Object.keys(br).length&&await uh().catch(()=>{})}}return br}).finally(()=>ah=null),ah)}async function uh(){let e=JSON.stringify({tickets:br},null,2),t=`${lh()}.${process.pid}.tmp`;await ki.mkdir(Dv(),{recursive:!0}),await ki.writeFile(t,e),await ki.rename(t,lh())}async function kr(e=null){if(ih=ih.then(uh,uh),await ih.catch(()=>{}),!e)return;let t=br?.[e];t?et("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Rt("tickets",e).catch(()=>{})}async function an(e){return(await Si())[e]??null}async function jv(){return{...await Si()}}async function Lv(){return(await fh()).length>0}async function Es(e,t,{source:n="api"}={}){let r=await fh();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let f=u.api.parseIssueRef(t);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Si())[e.id]=c,await kr(e.id),await _({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await xi(e.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await bi(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Hv(e.id,c,"linked"),c}async function mh(e){let t=await Si();return t[e]?(delete t[e],await kr(e),!0):!1}var dh=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function Fv(e,t){if(!e.track?.cwd)return null;let n=nr(await L({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function qB(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${dh(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${dh(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function KB(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(dh(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function xi(e,t){let n=await mo(t);if(!n)return;let r=await Fv(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:tu(e),title:`Bostrat Track: ${t.track.name}`,subtitle:KB(t,r)})}function nu(e){let t=new URL(Bv());return`${t.protocol}//${e}.${t.host}/`}var GB={linked:"linked",opened:"prOpened",merged:"prMerged"};function VB(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 Uv(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await kr(e),await _({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Hv(e,t,n){let r=await mo(t);if(!r)return;let o=r.cfg.ticketFlow?.[GB[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=ph(s,a),c=VB(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 Uv(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:Nv(),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 Wv(e){let t=await an(e);if(!t)throw new Error("no ticket linked to this Track");let n=await mo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=ph(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await kr(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function hh(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await an(e);if(!r)throw new Error("no ticket linked to this Track");let o=await mo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=ph(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await kr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Uv(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function qv(e,t){let n=await an(e);if(!n)throw new Error("no ticket linked to this Track");let r=await mo(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await _({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Kv(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 hh(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 Rv=50;async function Gv(){let e=await uo();if(!e)throw new Error("no ticketing integration configured");return As(e)}function zB(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>Rv)throw new Error(`too many issues (${n.length}); cap is ${Rv} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function JB(e,t,n,r){let o=r??await As(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await an(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(e.defaultTeam){let i=s(e.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function Vv(e,t){let n=await uo();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=zB(t),o=await As(n),s=await JB(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Nv();return await _({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function zv(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await L({id:e.id,cwd:e.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await _({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await uo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await As(a)).find(h=>h.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let m=await rh(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let h=await oh(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:m.title,message:h?.message||String(h)})}await _({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await _({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await an(e.id)&&(p=await Es(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function bi(e,t){let n=await mo(e);n&&await n.api.createComment(n.cfg,e,t)}async function YB(e,t,n){let r=await mo(e);if(!r)return null;let o=await An(t);if(!o)return null;let s=Dt(o,n);if(!s)return null;let a=await ki.readFile(Tt(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 $v=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),ch=new Map;function XB(e,t){if($v()<=0)return xi(e,t);if(ch.has(e))return;let n=setTimeout(()=>{ch.delete(e),an(e).then(r=>r?xi(e,r):null).catch(()=>{})},$v());n.unref?.(),ch.set(e,n)}var Ov=new Map;async function Cv(e,t,n){let r=Ov.get(e)??0;Date.now()-r<600*1e3||(Ov.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 ZB(e,{resolveTrack:t=null}={}){let n=await an(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await fh())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 Es(s,o,{source:"chat"})}catch(a){await Cv(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await kr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await kr(e.trackId),await xi(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await kr(e.trackId),await xi(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Fv(n,e.trackId).catch(()=>null):null;await bi(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
557
559
|
|
|
558
|
-
Agent cost for this Track: ${
|
|
560
|
+
Agent cost for this Track: ${qB(o)}.`:"")),await Hv(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await YB(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await bi(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
559
561
|
|
|
560
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
561
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
562
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
562
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await bi(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 bi(n,`**Bostrat** \u2014 live preview **${r.name}**: ${nu(r.slug)}
|
|
563
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await XB(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await Cv(e.trackId,s?.cwd??null,o)}}function Jv({resolveTrack:e}){st.on("event",t=>{ZB(t,{resolveTrack:e}).catch(()=>{})})}async function Yv(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Si(),r=0;for(let[o,s]of Object.entries(n))await et("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}Yo();us();import QB from"ws";var Xv=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function ru(e,t,n={}){let r=await fetch(`${Xv()}/${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 wh=(e,t)=>ru("chat.postMessage",e,t),ho=(e,t)=>ru("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 e1(e,t,n={}){let r=await fetch(`${Xv()}/${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 Zv(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await e1("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 ru("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var t1=2200;function Qv({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 ru("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new QB(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=h=>{try{o.send(JSON.stringify(h?{envelope_id:f.envelope_id,payload:h}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},t1);Promise.resolve().then(()=>t(f)).then(h=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?h:void 0))},h=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${h?.message||h}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var nT=()=>process.env.BOSTRAT_STATE_DIR||su.join(process.env.HOME||r1.tmpdir(),".bostrat"),xh=()=>su.join(nT(),"slack.json"),ou=null,gh=null,yh=Promise.resolve();async function vh(){return ou||(gh??=vi.readFile(xh(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>ou=e).finally(()=>gh=null),gh)}async function eT(){let e=JSON.stringify({threads:ou},null,2),t=`${xh()}.${process.pid}.tmp`;await vi.mkdir(nT(),{recursive:!0}),await vi.writeFile(t,e),await vi.rename(t,xh())}async function Lt(){yh=yh.then(eT,eT),await yh.catch(()=>{})}async function Th(e){return(await vh())[e]??null}async function o1(e,t){let n=await vh();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var ce=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Ti(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function s1(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Ti(n.replace(/\n+/g," "),t)}function rT(e){let t=`*${ce(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${ce(e.repoLabel)} \xB7 \`${ce(e.branch||"?")}\` \xB7 lead *${ce(e.lead)}*`,r=e.kickoff?`> ${ce(Ti(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
564
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${tu(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Sh(e,t){await ho(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:rT(t)})}async function Sr(e,t,n,r=void 0){return wh(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var tT=new Set;function xr(e){tT.add(e),e.catch(()=>{}).finally(()=>tT.delete(e))}var Te=e=>({response_type:"ephemeral",text:e}),a1="*/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._",i1=["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 kh=(e,t)=>Te(`No live Track matches \u201C${ce(t)}\u201D. Live: ${e.map(n=>`\`${ce(n.name)}\``).join(" \xB7 ")||"none"}`);async function oT(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=t.maxConcurrent){await 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?su.basename(c.base):"fresh workspace"),lead:"claude"},u=await wh(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:rT(l),unfurl_links:!1});l.ts=u.ts,(await vh())[i.id]=l,await Lt(),await rt(o,{...Te(`\u2713 *${ce(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 c1(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Th(n.id);s&&(s.archivedAt=Date.now(),await Lt(),await Sh(t,s).catch(()=>{})),await rt(r,{...Te(`\u2713 *${ce(n.name)}* archived.`),replace_original:!0})}catch(s){await rt(r,{...Te(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function l1(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await rt(o,{...Te(`\u2713 Handing *${ce(n.name)}* to ${ce(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await rt(o,{...Te(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function u1(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 *${ce(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await rt(r,{...Te(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function d1(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Ti(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:e}),text:{type:"mrkdwn",text:`Repo for *${ce(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function f1(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?su.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${ce(s.name)}* \u2014 ${ce(i)}${a.branch?` \`${ce(a.branch)}\``:""} \xB7 lead ${ce(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
563
565
|
`)+(o?`
|
|
564
|
-
_${o} archived_`:"")}async function
|
|
565
|
-
\u2713 *${
|
|
566
|
-
\u2713 _answered_`}}]})}async function
|
|
567
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
568
|
-
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&t.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...t.values()]}function
|
|
569
|
-
`)}ep();Nc();le();Tf();import Ej from"node:fs/promises";import _j from"node:path";gd();Pr();var Ql=e=>e.payload?.ts??e.ts,vj=12e4,gr=e=>Math.round(e*1e4)/1e4,Sj=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function Tj(e,{since:t}){let{track:n,events:r,brief:o}=e,s=v=>Ql(v)>=t,a=Nr([la(n?.name),la(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let v of r)if(v.type==="subtask.delegated"&&v.boxId&&v.payload?.task){let R=Nr(v.payload.task);R&&i.set(v.boxId,R)}let c=new Map,l=v=>{let R=v||"unattributed";return c.has(R)||c.set(R,{segments:[u(null)],lastTs:null,provider:null}),c.get(R)},u=v=>({textCat:v,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((v,R)=>Ql(v)-Ql(R)),d=0,m=0,p=0,w={turns:0};for(let v of f){let R=l(v.boxId),A=R.segments[R.segments.length-1],V=Ql(v);switch(R.lastTs!=null&&V>R.lastTs&&s(v)&&(A.activeMs+=Math.min(V-R.lastTs,vj)),R.lastTs=V,v.provider&&(R.provider=v.provider),v.type){case"turn.user":{let O=v.payload?.text;if(v.source==="hook"||!String(O||"").trim())break;let M=u(Nr(O));R.segments.push(M),s(v)&&(M.turns+=1);break}case"peer.message":{let O=v.payload?.text;if(!String(O||"").trim())break;let M=u(Nr(O));R.segments.push(M),s(v)&&(M.turns+=1);break}case"tool.called":{if(v.source==="transcript")break;let O=v.payload?.tool;O&&(A.tools[O]=(A.tools[O]||0)+1),s(v)&&(A.toolCalls+=1);break}case"cost.recorded":{if(!s(v))break;let{usd:O,tokens:M,model:T,unpriced:q}=sn(v);q&&(w.turns+=1),A.usd+=O,M&&(A.tokens.input+=M.input,A.tokens.output+=M.output,A.tokens.cacheRead+=M.cacheRead,A.tokens.cacheWrite+=M.cacheWrite),A.models[T]=(A.models[T]||0)+O,v.provider&&(A.provider=v.provider);break}case"handoff.done":s(v)&&(d+=1);break;case"subtask.merged":s(v)&&(m+=1);break;case"error":s(v)&&v.payload?.kind==="subtask-failed"&&(p+=1);break;default:break}}let y=new Map,b=!1;for(let[v,R]of c)for(let A of R.segments){if(!(A.usd>0||A.activeMs>0||A.turns>0||A.toolCalls>0))continue;let O=A.textCat?.category??i.get(v)?.category??Og(A.tools)?.category??a?.category??"uncategorized";y.has(O)||y.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let M=y.get(O);M.usd+=A.usd,M.activeMs+=A.activeMs,M.turns+=A.turns,M.toolCalls+=A.toolCalls;for(let[q,G]of Object.entries(A.models))M.models[q]=(M.models[q]||0)+G;let T=A.provider||R.provider;T&&(M.providers[T]=(M.providers[T]||0)+A.usd),T==="cursor"&&A.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:m,failed:p,sawCursorActivity:b,unpricedTurns:w.turns}}function YS(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let w of e||[]){let{track:y,events:b}=w;if(!b?.length){a+=1;continue}let{perCategory:v,handoffs:R,merged:A,failed:V,sawCursorActivity:O,unpricedTurns:M}=Tj(w,{since:r});O&&(i=!0),c+=M;let T={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:R,subtasksMerged:A,subtasksFailed:V};for(let[q,G]of v){T.usd+=G.usd,T.activeMs+=G.activeMs,T.turns+=G.turns,T.categories[q]={usd:gr(G.usd),activeMs:G.activeMs};for(let[N,$]of Object.entries(G.models))T.models[N]=gr((T.models[N]||0)+$);for(let[N,$]of Object.entries(G.providers))T.providers[N]=gr((T.providers[N]||0)+$);o.has(q)||o.set(q,Sj());let ue=o.get(q);ue.usd+=G.usd,ue.activeMs+=G.activeMs,ue.turns+=G.turns,ue.toolCalls+=G.toolCalls;for(let[N,$]of Object.entries(G.models))ue.models[N]=(ue.models[N]||0)+$;for(let[N,$]of Object.entries(G.providers))ue.providers[N]=(ue.providers[N]||0)+$;let Q=ue.byTrack.get(y?.id)||{usd:0,activeMs:0};Q.usd+=G.usd,Q.activeMs+=G.activeMs,ue.byTrack.set(y?.id,Q)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=gr(T.usd),T.dominantCategory=Aj(T.categories),s.push(T))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,m=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),p=[...o.entries()].map(([w,y])=>({category:w,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:gr(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:JS(y.models),providers:JS(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((b,v)=>v[1].usd-b[1].usd||v[1].activeMs-b[1].activeMs).slice(0,2).map(([b,v])=>({trackId:b,name:m.get(b),usd:gr(v.usd),activeMs:v.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:gr(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:p,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var Aj=e=>{let t=null;for(let[n,r]of Object.entries(e))(!t||r.usd>t.v.usd||r.usd===t.v.usd&&r.activeMs>t.v.activeMs)&&(t={cat:n,v:r});return t?.cat??null},JS=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,gr(n)]));var Es={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Ij(e){let t=String(e||"7d");return t in Es?{key:t,ms:Es[t]}:{key:"7d",ms:Es["7d"]}}function Rj(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=Dc(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 $j(e){if(!e?.cwd)return null;try{let n=(await Ej.readFile(_j.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 XS(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}:Ij(t),f=[];for(let d of e||[]){let m=await B(d),p=Rj(d,m);i&&c<o&&(p=p.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:p,brief:await $j(d)})}return{window:l,...YS(f,{windowMs:u,now:c})}}le();Pr();var yr=36e5,tT=24*yr,Oj=400,Cj=12,_s=e=>Math.round(e*1e4)/1e4;function Mj({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in Es?String(e||"7d"):"7d",i=Es[a];return{key:a,since:i==null?null:r-i,until:r}}var Nj=(e,t)=>e!=null&&t-e<=48*yr?yr:tT;function ZS(e,t){if(t===yr)return Math.floor(e/yr)*yr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function Pj(e,t){if(t===yr)return e+yr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var QS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),eT=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Bj=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,_s(n)]));async function nT(e,t={}){let n=t.now??Date.now(),r=Mj({...t,now:n}),o=r.since==null?tT:Nj(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=QS(),f=0,d=0,m=0,p=0,w=0,y=new Set,b=new Map,v=[],R=[],A=[],V=null;for(let T of e||[]){let G=(await B(T)).filter(N=>N.type==="cost.recorded");if(!G.length){T.archivedAt||A.push(T.name||T.id);continue}let ue=[];for(let N of G){let $=N.ts??0;s($)?ue.push(N):i($)&&(w+=sn(N).usd)}if(!ue.length)continue;let Q=Vn(ue,{boxLabels:ph(T)});f+=Q.totalUsd,d+=Q.turns,m+=Q.repricedTurns||0,p+=Q.unpricedTurns||0;for(let N of Q.unpricedModels||[])y.add(N);eT(u,Q.totalTokens);for(let[N,$]of Object.entries(Q.byModel))c[N]||={usd:0,events:0,tokens:QS()},c[N].usd=_s(c[N].usd+$.usd),c[N].events+=$.events,eT(c[N].tokens,$.tokens);for(let[N,$]of Object.entries(Q.byProvider))l[N]||={usd:0,events:0},l[N].usd=_s(l[N].usd+$.usd),l[N].events+=$.events;for(let N of ue){let{usd:$}=sn(N);if(!$)continue;let F=ZS(N.ts??0,o),ce=b.get(F)??{t:F,total:0,byProvider:{}};ce.total+=$;let Re=N.provider||"unknown";ce.byProvider[Re]=(ce.byProvider[Re]||0)+$,b.set(F,ce),(V==null||F<V)&&(V=F)}v.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:Q.totalUsd,turns:Q.turns,providers:Object.keys(Q.byProvider)});for(let[N,$]of Object.entries(Q.byBox))R.push({trackId:T.id,trackName:T.name||T.id,boxId:N,...$})}let O=r.since!=null?ZS(r.since,o):V,M=[];for(let T=O,q=0;T!=null&&T<r.until&&q<Oj;T=Pj(T,o),q++){let G=b.get(T);M.push(G?{t:T,total:_s(G.total),byProvider:Bj(G.byProvider)}:{t:T,total:0,byProvider:{}})}return v.sort((T,q)=>q.usd-T.usd),R.sort((T,q)=>q.usd-T.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:M,fleet:{totalUsd:_s(f),turns:d,repricedTurns:m,unpricedTurns:p,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:v.length,tracksSilentLive:A},prior:a!=null?{totalUsd:_s(w),since:r.since-a,until:r.since}:null,tracks:v.slice(0,50),boxes:R.slice(0,Cj)}}Je();import Is from"node:fs/promises";import Dj from"node:fs";import Ge from"node:path";import jj from"node:os";import{execFile as Lj}from"node:child_process";import{promisify as Fj}from"node:util";var Uj=Fj(Lj),rT=process.env.BOSTRAT_STATE_DIR||Ge.join(process.env.HOME||jj.tmpdir(),".bostrat"),eu=!1,mh=null;async function hh(e,t){let n=await Is.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await Is.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Is.mkdir(Ge.dirname(t),{recursive:!0}),await Is.copyFile(e,t),!0)}var Hj=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 tu(e,t,{skip:n=null}={}){let r=0,o=await Is.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(Hj.has(s.name)||n?.has(s.name))continue;let a=Ge.join(e,s.name);s.isDirectory()?r+=await tu(a,Ge.join(t,s.name)):s.isFile()&&(r+=await hh(a,Ge.join(t,s.name))?1:0)}return r}async function oT({stateDir:e=rT,dest:t=process.env.BOSTRAT_SYNC_DIR||`${rT}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Is.mkdir(t,{recursive:!0});let o=0;o+=await hh(Ge.join(e,"tracks.json"),Ge.join(t,"tracks.json"))?1:0,o+=await hh(Ge.join(e,"events.jsonl"),Ge.join(t,"events.jsonl"))?1:0,o+=await tu(Ge.join(e,"events"),Ge.join(t,"events")),o+=await tu(Ge.join(e,"memory"),Ge.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await tu(Ge.join(u.cwd,".bostrat"),Ge.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>Uj("git",["-C",t,...u]);Dj.existsSync(Ge.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 Iu(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||eu))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,eu&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),eu=!1,mh=null}catch(u){eu=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
570
|
-
`).slice(-3).join(" | ").slice(0,300);f!==
|
|
571
|
-
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,t.length)&&!/^Current\s/i.test(t[c]);c++){let l=t[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=t[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=t.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return t.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function
|
|
572
|
-
`).map(s=>s.replace(/\s+$/,"")),n=t.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<t.length&&!r.test(t[s]);s++)t[s].trim()&&o.push(t[s].trim());return o.length?o:null}async function
|
|
573
|
-
`),r=!1;for(let o=n.length-1;o>=0;o--){let s=n[o].trim();if(!s||s[0]!=="{"||!s.includes("token_count"))continue;let a;try{a=JSON.parse(s)}catch{continue}let i=a?.payload;if(i?.type!=="token_count")continue;r=!0;let c=i.rate_limits;if(!c)return
|
|
574
|
-
`,{mode:384}),
|
|
566
|
+
_${o} archived_`:"")}async function p1(e,t){let n=await fo();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(f=>f.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(xr(oT(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Te(`Starting *${ce(u)}* on ${ce(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:d1(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?(xr(c1(e,n,{track:i,responseUrl:t.response_url})),Te(`Archiving *${ce(i.name)}*\u2026`)):kh(c,a.join(" "))}case"handoff":{let i=r.knownAgents??i1,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:f}=bh(await r.listTracks(),l);return u?(xr(l1(e,n,{track:u,agent:c,responseUrl:t.response_url})),Te(`Handing *${ce(u.name)}* to ${ce(c)}\u2026`)):kh(f,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?(xr(u1(e,n,{track:i,responseUrl:t.response_url})),Te(`Stopping *${ce(i.name)}*\u2026`)):kh(c,a.join(" "))}case"list":return Te(await f1(r));default:return Te(a1)}}async function m1(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Th(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 Lt(),await ho(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ce(l||"question")}~
|
|
567
|
+
\u2713 *${ce(a)}*`}}]}).catch(()=>{})}}catch(c){await rt(n.response_url,Te(`Couldn't answer: ${c?.message||c}`))}}async function h1(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(d=>d.type==="pr.offered"&&d.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===s))throw new Error("offer already answered");let f=Array.isArray(r.actions)?zo.filter(d=>r.actions.includes(d)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(f?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:f}))return;f&&await i.sayToLead(c.id,Jo(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:f?a:"dismiss",actions:f}})}catch(c){await rt(n.response_url,Te(`Couldn't act on the offer: ${c?.message||c}`))}}async function w1(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 g1(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await fo();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 *${ce(s)}*\u2026`),replace_original:!0}),xr(oT(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"?xr(m1(e,r,t,o)):n.action_id==="bostrat-ship"?xr(h1(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&xr(w1(e,r,t,o))}async function y1(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 o1(t.channel,t.thread_ts);if(!n)return;let r=await fo();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Sr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function b1(e){return async t=>{if(t.type==="slash_commands")return p1(e,t.payload);if(t.type==="interactive"){await g1(e,t.payload);return}if(t.type==="events_api"){await y1(e,t.payload?.event);return}}}var k1=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 x1(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${ce(t.question||"The lead needs an answer")}`}}],r=(t.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:Ti(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 S1(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await ho(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ce(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Sr(e,t,r.question||"The lead needs an answer",x1(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Lt()}async function v1(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 Lt(),await ho(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ce(s||"question")}~
|
|
568
|
+
\u2713 _answered_`}}]})}async function T1(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(f=>l[f]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await Sr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${ce(Ti(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Lt()}async function A1(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Lt();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 ho(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function E1(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 Sr(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${ce(r.branch)})`:""} is already open. Push the update to it, or open a new PR?`}},{type:"actions",elements:[s("push",`Push to ${o}`,"primary"),s("new","Open a new PR"),s("dismiss","Not now")]}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Lt()}async function _1(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Lt();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await ho(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function I1(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??R1)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Zv(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${ce(o.name)}*${a}${o.group?` (group \`${ce(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${ce(a.name)}*`).join(", ");await Sr(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${ce(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function R1(e,t){let n=await An(e);if(!n)return null;let r=Dt(n,t);if(!r)return null;let o=await vi.readFile(Tt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function $1(e,t){if(!k1.has(t.type))return;let n=await Th(t.trackId);if(!n)return;let r=await fo();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 Lt(),await Sh(r,n);return;case"turn.agent":{if(!o.text)return;let s=s1(o.text);if(!s)return;await Sr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:ce(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${tu(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return S1(r,n,t);case"prompt.answered":return v1(r,n,t);case"pr.offered":return T1(r,n,t);case"pr.offer.answered":return A1(r,n,t);case"pr.exists.offered":return E1(r,n,t);case"pr.exists.answered":return _1(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 Sr(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 Lt(),await Sh(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Sr(r,n,`Live preview ${o.name}: ${nu(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${ce(o.name)}*: ${nu(o.slug)}
|
|
569
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return I1(r,n,t,e.deps)}}function sT(e){let t={deps:e};st.on("event",s=>{$1(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await fo()||(r&&clearInterval(r),r=null,n=Qv({getToken:async()=>(await fo())?.appToken,onEnvelope:b1(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}Mt();ue();un();Qf();xa();import O1 from"node:fs/promises";function C1(e){let t=new Map;for(let n of String(e||"").split(`
|
|
570
|
+
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&t.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...t.values()]}function M1(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 N1=new Set(["Write","Edit","NotebookEdit"]),P1=220,D1=160;function B1(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(N1.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,P1)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Cr(o.command.trim().replace(/\s+/g," ")).slice(0,D1));return r}function j1(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Ct(t.cwd,t.sessionId):t?.cwd?id(t.cwd):null}async function aT(e,t){let n=await t(e?.session_id,e?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await _(r,{...s,...c}))};switch(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=cl(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:B1(e,o)});break;case"Stop":await L1(r,o,e,i);break;default:break}return a}async function L1(e,t,n,r){let o=[],s=j1(n,t);if(s)try{o=C1(await O1.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=M1(await L(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=f.model||"unknown",p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=f.tokens[m];p.messageIds.push(f.messageId),u.set(d,p)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:tr(f,d.tokens),tokens:d.tokens,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}Fs();import F1 from"node:fs";import iT from"node:fs/promises";import Ai from"node:os";import U1 from"node:path";Ep();tt();var H1=1024*1024,au=e=>Math.round(e/H1);async function W1(e){try{let t=await iT.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:au(r),diskTotalMb:au(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function q1(){try{return(await $t()).filter(t=>t.isBostrat).length}catch{return null}}async function K1(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await iT.readdir(U1.join(hi,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function cT({stateDir:e=hi,now:t=Date.now}={}){let n=Ai.totalmem(),[r,o,s,a]=await Promise.all([kl().catch(()=>null),W1(F1.existsSync(e)?e:Ai.tmpdir()),q1(),K1()]),i=Ai.cpus()?.length||null,c=Ai.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?au(n-r):null,memTotalMb:au(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Ai.uptime())}}ue();Rd();zs();ue();Fr();function Ah(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 lT=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),uT=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},iu=e=>Math.round(e*1e4)/1e4;async function dT(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=lT(),f=0,d=[];for(let p of e||[]){let m=await L(p),h=nr(m,{boxLabels:Ah(p)});if(h.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.asRecordedUsd||0,c+=h.unpricedTurns||0;for(let g of h.unpricedModels||[])l.add(g);uT(u,h.totalTokens);for(let[g,x]of Object.entries(h.byModel))n[g]||={usd:0,events:0,tokens:lT()},n[g].usd=iu(n[g].usd+x.usd),n[g].events+=x.events,uT(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(h.byProvider))r[g]||={usd:0,events:0},r[g].usd=iu(r[g].usd+x.usd),r[g].events+=x.events;t.push({id:p.id,name:p.name,usd:h.totalUsd,rollup:h})}return t.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:iu(o),asRecordedUsd:iu(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:t}}var vr=e=>`$${e.toFixed(4)}`,_s=(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||[];r.push(`fleet total ${vr(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 ${vr(t.asRecordedUsd)} \xB7 current-rates total ${vr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${vr(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${_s(a,26)} ${_s(vr(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(` ${_s(a.name||a.id,30)} ${vr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${_s(c.label,40)} ${_s(vr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${_s(vr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
571
|
+
`)}lp();Hc();ue();Mf();import Y1 from"node:fs/promises";import X1 from"node:path";Id();Fr();var cu=e=>e.payload?.ts??e.ts,G1=12e4,Tr=e=>Math.round(e*1e4)/1e4,V1=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function z1(e,{since:t}){let{track:n,events:r,brief:o}=e,s=k=>cu(k)>=t,a=Lr([wa(n?.name),wa(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let k of r)if(k.type==="subtask.delegated"&&k.boxId&&k.payload?.task){let A=Lr(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}),f=[...r].sort((k,A)=>cu(k)-cu(A)),d=0,p=0,m=0,h={turns:0};for(let k of f){let A=l(k.boxId),R=A.segments[A.segments.length-1],M=cu(k);switch(A.lastTs!=null&&M>A.lastTs&&s(k)&&(R.activeMs+=Math.min(M-A.lastTs,G1)),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 C=u(Lr(N));A.segments.push(C),s(k)&&(C.turns+=1);break}case"peer.message":{let N=k.payload?.text;if(!String(N||"").trim())break;let C=u(Lr(N));A.segments.push(C),s(k)&&(C.turns+=1);break}case"tool.called":{if(k.source==="transcript")break;let N=k.payload?.tool;N&&(R.tools[N]=(R.tools[N]||0)+1),s(k)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(k))break;let{usd:N,tokens:C,model:T,unpriced:j}=pn(k);j&&(h.turns+=1),R.usd+=N,C&&(R.tokens.input+=C.input,R.tokens.output+=C.output,R.tokens.cacheRead+=C.cacheRead,R.tokens.cacheWrite+=C.cacheWrite),R.models[T]=(R.models[T]||0)+N,k.provider&&(R.provider=k.provider);break}case"handoff.done":s(k)&&(d+=1);break;case"subtask.merged":s(k)&&(p+=1);break;case"error":s(k)&&k.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let g=new Map,x=!1;for(let[k,A]of c)for(let R of A.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let N=R.textCat?.category??i.get(k)?.category??zg(R.tools)?.category??a?.category??"uncategorized";g.has(N)||g.set(N,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let C=g.get(N);C.usd+=R.usd,C.activeMs+=R.activeMs,C.turns+=R.turns,C.toolCalls+=R.toolCalls;for(let[j,Z]of Object.entries(R.models))C.models[j]=(C.models[j]||0)+Z;let T=R.provider||A.provider;T&&(C.providers[T]=(C.providers[T]||0)+R.usd),T==="cursor"&&R.usd===0&&(x=!0)}return{perCategory:g,handoffs:d,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:R,failed:M,sawCursorActivity:N,unpricedTurns:C}=z1(h,{since:r});N&&(i=!0),c+=C;let T={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:A,subtasksMerged:R,subtasksFailed:M};for(let[j,Z]of k){T.usd+=Z.usd,T.activeMs+=Z.activeMs,T.turns+=Z.turns,T.categories[j]={usd:Tr(Z.usd),activeMs:Z.activeMs};for(let[D,I]of Object.entries(Z.models))T.models[D]=Tr((T.models[D]||0)+I);for(let[D,I]of Object.entries(Z.providers))T.providers[D]=Tr((T.providers[D]||0)+I);o.has(j)||o.set(j,V1());let fe=o.get(j);fe.usd+=Z.usd,fe.activeMs+=Z.activeMs,fe.turns+=Z.turns,fe.toolCalls+=Z.toolCalls;for(let[D,I]of Object.entries(Z.models))fe.models[D]=(fe.models[D]||0)+I;for(let[D,I]of Object.entries(Z.providers))fe.providers[D]=(fe.providers[D]||0)+I;let ie=fe.byTrack.get(g?.id)||{usd:0,activeMs:0};ie.usd+=Z.usd,ie.activeMs+=Z.activeMs,fe.byTrack.set(g?.id,ie)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=Tr(T.usd),T.dominantCategory=J1(T.categories),s.push(T))}let l=[...o.values()].reduce((h,g)=>h+g.usd,0),u=[...o.values()].reduce((h,g)=>h+g.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((e||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,g])=>({category:h,sharePct:d>0?Math.round((f==="usd"?g.usd:g.activeMs)/d*100):0,shareBasis:f,usd:Tr(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:pT(g.models),providers:pT(g.providers),topWorkstreams:[...g.byTrack.entries()].sort((x,k)=>k[1].usd-x[1].usd||k[1].activeMs-x[1].activeMs).slice(0,2).map(([x,k])=>({trackId:x,name:p.get(x),usd:Tr(k.usd),activeMs:k.activeMs}))})).sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs);return s.sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:Tr(l),activeMs:u,turns:[...o.values()].reduce((h,g)=>h+g.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var J1=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},pT=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Tr(n)]));var Is={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Z1(e){let t=String(e||"7d");return t in Is?{key:t,ms:Is[t]}:{key:"7d",ms:Is["7d"]}}function Q1(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=Kc(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 ej(e){if(!e?.cwd)return null;try{let n=(await Y1.readFile(X1.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 hT(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}:Z1(t),f=[];for(let d of e||[]){let p=await L(d),m=Q1(d,p);i&&c<o&&(m=m.filter(h=>(h.ts??0)<=c)),f.push({track:d,events:m,brief:await ej(d)})}return{window:l,...mT(f,{windowMs:u,now:c})}}ue();Fr();var Ar=36e5,bT=24*Ar,tj=400,nj=12,Rs=e=>Math.round(e*1e4)/1e4;function rj({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in Is?String(e||"7d"):"7d",i=Is[a];return{key:a,since:i==null?null:r-i,until:r}}var oj=(e,t)=>e!=null&&t-e<=48*Ar?Ar:bT;function wT(e,t){if(t===Ar)return Math.floor(e/Ar)*Ar;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function sj(e,t){if(t===Ar)return e+Ar;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}),yT=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},aj=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Rs(n)]));async function kT(e,t={}){let n=t.now??Date.now(),r=rj({...t,now:n}),o=r.since==null?bT:oj(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=gT(),f=0,d=0,p=0,m=0,h=0,g=new Set,x=new Map,k=[],A=[],R=[],M=null;for(let T of e||[]){let Z=(await L(T)).filter(D=>D.type==="cost.recorded");if(!Z.length){T.archivedAt||R.push(T.name||T.id);continue}let fe=[];for(let D of Z){let I=D.ts??0;s(I)?fe.push(D):i(I)&&(h+=pn(D).usd)}if(!fe.length)continue;let ie=nr(fe,{boxLabels:Ah(T)});f+=ie.totalUsd,d+=ie.turns,p+=ie.repricedTurns||0,m+=ie.unpricedTurns||0;for(let D of ie.unpricedModels||[])g.add(D);yT(u,ie.totalTokens);for(let[D,I]of Object.entries(ie.byModel))c[D]||={usd:0,events:0,tokens:gT()},c[D].usd=Rs(c[D].usd+I.usd),c[D].events+=I.events,yT(c[D].tokens,I.tokens);for(let[D,I]of Object.entries(ie.byProvider))l[D]||={usd:0,events:0},l[D].usd=Rs(l[D].usd+I.usd),l[D].events+=I.events;for(let D of fe){let{usd:I}=pn(D);if(!I)continue;let F=wT(D.ts??0,o),pe=x.get(F)??{t:F,total:0,byProvider:{}};pe.total+=I;let Se=D.provider||"unknown";pe.byProvider[Se]=(pe.byProvider[Se]||0)+I,x.set(F,pe),(M==null||F<M)&&(M=F)}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,I]of Object.entries(ie.byBox))A.push({trackId:T.id,trackName:T.name||T.id,boxId:D,...I})}let N=r.since!=null?wT(r.since,o):M,C=[];for(let T=N,j=0;T!=null&&T<r.until&&j<tj;T=sj(T,o),j++){let Z=x.get(T);C.push(Z?{t:T,total:Rs(Z.total),byProvider:aj(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)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:C,fleet:{totalUsd:Rs(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...g],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:k.length,tracksSilentLive:R},prior:a!=null?{totalUsd:Rs(h),since:r.since-a,until:r.since}:null,tracks:k.slice(0,50),boxes:A.slice(0,nj)}}Qe();import $s from"node:fs/promises";import ij from"node:fs";import ze from"node:path";import cj from"node:os";import{execFile as lj}from"node:child_process";import{promisify as uj}from"node:util";var dj=uj(lj),xT=process.env.BOSTRAT_STATE_DIR||ze.join(process.env.HOME||cj.tmpdir(),".bostrat"),lu=!1,Eh=null;async function _h(e,t){let n=await $s.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await $s.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await $s.mkdir(ze.dirname(t),{recursive:!0}),await $s.copyFile(e,t),!0)}var fj=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 uu(e,t,{skip:n=null}={}){let r=0,o=await $s.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(fj.has(s.name)||n?.has(s.name))continue;let a=ze.join(e,s.name);s.isDirectory()?r+=await uu(a,ze.join(t,s.name)):s.isFile()&&(r+=await _h(a,ze.join(t,s.name))?1:0)}return r}async function ST({stateDir:e=xT,dest:t=process.env.BOSTRAT_SYNC_DIR||`${xT}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await $s.mkdir(t,{recursive:!0});let o=0;o+=await _h(ze.join(e,"tracks.json"),ze.join(t,"tracks.json"))?1:0,o+=await _h(ze.join(e,"events.jsonl"),ze.join(t,"events.jsonl"))?1:0,o+=await uu(ze.join(e,"events"),ze.join(t,"events")),o+=await uu(ze.join(e,"memory"),ze.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await uu(ze.join(u.cwd,".bostrat"),ze.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>dj("git",["-C",t,...u]);ij.existsSync(ze.join(t,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Bu(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||lu))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,lu&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),lu=!1,Eh=null}catch(u){lu=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
572
|
+
`).slice(-3).join(" | ").slice(0,300);f!==Eh&&(Eh=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}Kp();Mt();Br();tt();Us();import{execFile as pj}from"node:child_process";import{promisify as mj}from"node:util";import{randomUUID as hj}from"node:crypto";import wj from"node:fs";import gj from"node:os";import yj from"node:path";var _r=mj(pj),du=e=>new Promise(t=>setTimeout(t,e)),Ir=td,Ih=process.env.BOSTRAT_USAGE_PROBE_CWD||yj.join(gj.homedir(),".bostrat","usage-probe"),bj=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,kj=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,xj=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,vT=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Er=null,Ei=null,_i=null;async function Sj(){try{return await _r(U,K("has-session","-t",Ir)),!0}catch{return!1}}function vj(){Er&&clearTimeout(Er),Er=setTimeout(()=>Tj().catch(()=>{}),bj),Er.unref&&Er.unref()}async function Tj(){Er&&(clearTimeout(Er),Er=null);try{await _r(U,K("kill-session","-t",Ir))}catch{}}async function Aj(){let e=Date.now()+xj;for(;;){let t=await Rh().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await du(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await du(500)}}async function Ej(){if(await Sj())return;if(!await Vn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");wj.mkdirSync(Ih,{recursive:!0}),await Ln(Ih);let e=hj();await _r(U,K("new-session","-d","-s",Ir,"-c",Ih,"-x","200","-y","60",...na()));let t=`${ed} --session-id ${e}`;await _r(U,K("send-keys","-t",Ir,t,"Enter")),await Aj()}async function Rh(){let{stdout:e}=await _r(U,K("capture-pane","-t",Ir,"-p"));return e}async function _j(){await Ej(),await _r(U,K("send-keys","-t",Ir,"-l","/usage")),await _r(U,K("send-keys","-t",Ir,"Enter"));let e=Date.now()+kj,t="";for(;await du(400),t=await Rh(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await du(900),t=await Rh(),await _r(U,K("send-keys","-t",Ir,"Escape")).catch(()=>{}),vj(),t}function Ij(e){let t=String(e||"").split(`
|
|
573
|
+
`).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 Rj(e){let t=String(e||"").split(`
|
|
574
|
+
`).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 TT({fresh:e=!1}={}){return!e&&Ei&&Date.now()-Ei.at<vT?Ei.data:_i||(_i=(async()=>{try{let t=await _j(),n=Ij(t),r={...n,contributors:Rj(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ei={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 Ei={at:Date.now()-(vT-3e3),data:n},n}finally{_i=null}})(),_i)}var $j=262144;function Oj(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 Cj=new Set([300]),Mj=new Set([10080]);function Nj(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:Cj.has(t)?"session":Mj.has(t)?"week":"other"}function Pj(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 Dj(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?Oj((o-n)/1e3):null,resetsAt:o}}var Ii=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function Bj(e,t=Date.now()){let n=String(e||"").split(`
|
|
575
|
+
`),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 Ii(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Ii(r);[c.primary,c.secondary].forEach((p,m)=>{let h=Dj(p,l,t);if(!h)return;let g=Nj(p.window_minutes);g===null&&(g=m===0?"session":"week"),g==="session"&&!u.session?u.session=h:g==="week"&&!u.week?u.week=h:(u.other??=[]).push({label:Pj(p.window_minutes),...h})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let d=c.credits;return d&&(d.has_credits||d.unlimited)&&(u.credits={balance:String(d.balance??""),unlimited:!!d.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...Ii(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Ii(r)}return Ii(r)}async function $h(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Pr(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 it(n.file,$j),o=Bj(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 AT(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 fu({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 $h(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await $h(null))}return n.has("cursor")&&r.push(await fu({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function fu(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?$h(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await TT({fresh:t})}}Un();Qe();Qe();function jj(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}var Lj=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),pu=(e,t)=>Object.assign(new Error(e),{status:t});async function ET(e,t){if(!jj(t))throw pu("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${ke(t)}${e}`,{headers:Lj(t)})}catch(o){throw pu(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw pu(r?.error||`plane replied ${n.status}`,n.status);return r}async function _T(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await ET(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function IT(e,{env:t=process.env}={}){if(!e)throw pu("message id required",400);return(await ET(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}Ca();nl();function Oh(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 Fj(e){let[t,n]=e.split("/"),r=Oh(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 RT(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(Fj).filter(Boolean)}function Uj(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function $T(e){let t=RT(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:Uj,allowed(r){if(n)return!0;let o=Oh((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function OT(e,t){let n=RT(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Oh(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import CT from"node:crypto";import mu from"node:fs";import Hj from"node:path";function MT(e=process.env,{stateDir:t=hi,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=Hj.join(t,"api-token");try{let a=mu.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=CT.randomBytes(32).toString("base64url");try{mu.mkdirSync(t,{recursive:!0}),mu.writeFileSync(o,s+`
|
|
576
|
+
`,{mode:384}),mu.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Ri(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 NT(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:CT.timingSafeEqual(n,r)}function PT(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 Wj=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],qj=e=>Wj.some(t=>t.test(e));function DT({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=PT(o);return NT(c,e)?a():qj(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||Ri(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 BT(e,t,{enforce:n=!0}={}){return!n||NT(PT(e),t)?!0:Ri(e)}un();yl();yl();import Kj from"node:http";import Gj from"node:net";function hu(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 wu(e){let t=hu(e.headers.host);return t?{slug:t,view:xp(t)}:null}var Vj=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),zj=e=>{let t=Vj(e.name);return`<!doctype html><meta charset="utf-8">
|
|
575
577
|
<meta http-equiv="refresh" content="3">
|
|
576
578
|
<title>${t} \u2014 starting</title>
|
|
577
579
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
578
580
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
579
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
581
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function jT(e,t,n){let r=wu(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(zj(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=Kj.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 LT(e,t,n){let r=wu(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
|
|
580
582
|
\r
|
|
581
583
|
bostrat: no live view "${s}"\r
|
|
582
|
-
`),t.destroy(),!0;let a=
|
|
584
|
+
`),t.destroy(),!0;let a=Gj.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
|
|
583
585
|
`)+`\r
|
|
584
586
|
\r
|
|
585
|
-
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function
|
|
586
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,O1)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${C1}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=P1.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);if(i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`,i.length>$T)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${$T}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>RT)throw new Error(`${r.length} bullets \u2014 over the ${RT}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function CT(e,{repos:t=kh()}={}){let n=new Set(t);return(await Kr()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,mergedAt:r.resolvedAt??null}))}async function MT(e,{md:t,day:n=null}){let r=B1(t),o=n==null||n===""?N1():String(n);if(!M1.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await I({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}es();Qn();rs();le();es();Je();Jr();Qn();Ta();zc();Ra();import{execFile as D1}from"node:child_process";import{promisify as j1}from"node:util";var vh=j1(D1),NT=3e4,L1=30*6e4,F1=NT,U1=10*6e4,H1=10080*6e4,W1=60*6e4,q1=8,K1=1440*6e4,G1=3,PT="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",xh=60*6e4,V1=15*6e4,z1=9e4,J1=1440*6e4,cu=new Map,lu=new Map,Y1=/could not resolve|not found|no pull requests?/i,X1=e=>Y1.test(String(e?.message||e||""));function Z1(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<xh)}var BT={fast:F1,slow:U1,decay:W1};function DT(e,t){return e.armedAt&&t-e.armedAt<L1||Z1(e,t)||jT(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>H1?"decay":"slow"}function Q1(e,t){return BT[DT(e,t)]}function xi(e,t,n,r){let o=e.lanes?.get(X(t.repo,t.number))||{};return Vc({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function eL(e,t){un("watch");try{let{stdout:n}=await vh("gh",["pr","view",String(t),"--repo",e,"--json",PT],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Pt({slug:e})}});return JSON.parse(n||"null")}catch(n){throw tr(n)&&nr(n),n}}async function tL(e){un("watch");try{let{stdout:t}=await vh("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",PT],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Pt({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw tr(t)&&nr(t),t}}async function nL(){let e=new Map;for(let t of await oe().catch(()=>[]))e.set(t.id,t);return e}async function rL(e,t){await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function oL(e,t){let n=await Vr(e);return n?Zo(n,t):null}async function sL(e,t,n){let r=await Vr(e);if(!r)throw new Error(`no local checkout for ${e}`);return Qo(r,t,{method:n})}async function aL(e,t){let n=await Vr(e);if(!n)throw new Error(`no local checkout for ${e}`);return _a(n,t)}async function iL(e,t){await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function Rs(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 cL=10*6e4;async function lL(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>K1){await Rs(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 Rs(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Rs(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<cL)return;await Rs(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 Rs(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 Rs(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await LT(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function $s(e,t,n){await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function uL(e,t){try{return await vh("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function ki(e,t,n,r=()=>!0){return(await B(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Os=(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 jT(e,t,{grace:n=z1}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=J1}async function dL(e,t,n,r){let o=r.now??Date.now();if(!jT(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||s.archivedAt||s.transferredTo)return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??zr)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await B(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??cn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??cn)(e.repo,e.number).catch(()=>null)?(await $s(s,"opened",{...Os(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 fL(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 Lc(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Uc(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<V1;await Tk(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Sk(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await ki(a,"pushed",e.number,f=>f.payload?.sha===s)&&await $s(a,"pushed",{...Os(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>xh)await Fc(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 Fc(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=xi(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await ki(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await $s(a,"ci-fixed",{...Os(e,t),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Fc(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>xh)await Hc(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Hc(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){xi(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await ki(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await $s(a,"reconciled",{...Os(e,t),base:i.baseRefName||null}).catch(()=>{}),await Hc(e.repo,e.number).catch(()=>{})}}}}async function pL(e,t,n,r){let o=xi(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await ki(s,"closed",e.number)&&await $s(s,"closed",{...Os(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 mL(e,t,n){xi(n,e,"lost",null);let r=t.get(e.trackId);r&&!await ki(r,"lost",e.number)&&await $s(r,"lost",Os(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function LT(e,t,n,r,o={}){let s=xi(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await B(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function hL(e={}){let{now:t=Date.now(),auth:n=Mf,view:r=eL,list:o=tL,links:s=Ek,trackMap:a=nL,emitMerged:i=rL,mark:c=Zn,archiveIfArmed:l=fn,notifyIfArmed:u=pn,notifyIfResolved:f=Il,batchThreshold:d=G1,detail:m=oL,merge:p=sL,updateBranch:w=aL,lookup:y=Ze,disarm:b=Sa,emitDisarmed:v=iL,localHasCommit:R=uL,claim:A=cn,midTurn:V=zr}=e;if(!(await n().catch(()=>null))?.authenticated||Kc()>0)return 0;let T=(await s().catch(()=>[])).filter(F=>t-(cu.get(X(F.repo,F.number))||0)>=Q1(F,t));if(!T.length)return 0;let q=Date.now(),G=new Map,ue=new Map;for(let F of T){let ce=DT(F,t);G.set(X(F.repo,F.number),{lane:ce,intervalMs:BT[ce]}),cu.set(X(F.repo,F.number),t),ue.has(F.repo)||ue.set(F.repo,[]),ue.get(F.repo).push(F)}let Q=await a().catch(()=>new Map),N={now:t,lanes:G,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:m,merge:p,updateBranch:w,lookup:y,disarm:b,emitDisarmed:v,localHasCommit:R,claim:A,midTurn:V};for(let[F,ce]of ue){let Re,De=new Set;if(ce.length>d)Re=await o(F).catch(()=>null);else{Re=new Map;for(let be of ce)try{let g=await r(F,be.number);g?.number!=null&&Re.set(Number(g.number),g)}catch(g){X1(g)&&De.add(X(be.repo,be.number))}}if(Re)for(let be of ce){let g=X(be.repo,be.number),h=Re.get(Number(be.number));if(!h){if(De.has(g)){let k=(lu.get(g)||0)+1;lu.set(g,k),k>=q1&&(await mL(be,Q,N),lu.delete(g),cu.delete(g))}continue}lu.delete(g),h.state==="MERGED"||h.state==="CLOSED"?(h.state==="MERGED"?await LT(be,h,Q,N):await pL(be,h,Q,N),cu.delete(g)):(await dL(be,h,Q,N).catch(()=>{}),await fL(be,h,Q,N).catch(()=>{}),be.autoMerge&&await lL(be,Q,N).catch(()=>{}))}}let $={};for(let{lane:F}of G.values())$[F]=($[F]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries($).map(([F,ce])=>`${F} ${ce}`).join(", ")}) in ${Date.now()-q}ms`),T.length}function FT(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await hL()}catch{}finally{e=!1}}},NT)}Xe();le();Jr();import{randomUUID as wL}from"node:crypto";var gL=2e4,UT=3,Cs=new Map,Sh=new Set;async function HT(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 yL(e={}){let{sessions:t=null,tracks:n=null,append:r=I,read:o=B,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Tt(),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 f=(u.boxes||[]).find(m=>m.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let m=(Cs.get(f.name)||0)+1;Cs.set(f.name,m),m===UT&&(Sh.add(f.name),await HT(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:wL(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let m=(Cs.get(f.name)||0)>=UT;if(Cs.delete(f.name),m||!Sh.has(f.name)){Sh.add(f.name);let p=await HT(u,f.name,o);p&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:p,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Cs.keys()])l.has(u)||Cs.delete(u)}var vi=null,Th=!1;function WT(){return vi?()=>{}:(vi=setInterval(()=>{Th||(Th=!0,yL().catch(()=>{}).finally(()=>{Th=!1}))},gL),vi.unref?.(),()=>{clearInterval(vi),vi=null})}zo();hp();le();es();rs();zo();import{randomUUID as bL}from"node:crypto";async function kL(){let e=await Promise.resolve().then(()=>(Jr(),Fv));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Ah={listRepoPrs:Lt,readEvents:B,appendEvent:I,sayDeps:kL,currentBranch:ts};async function xL(e,t=Ah,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ts)(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 Eh(e,t,{actions:n,notify:r=!1}={},o=Ah){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 xL(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=bL();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 _h(e,{offerId:t,choice:n}={},r=Ah){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=Go.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Vo(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,pk(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 Rn from"node:fs/promises";import Ms from"node:path";import YT from"node:os";import qT from"node:fs/promises";import uu from"node:path";import{execFile as vL}from"node:child_process";import{promisify as SL}from"node:util";import{fileURLToPath as TL}from"node:url";var AL=SL(vL),GT=uu.dirname(TL(import.meta.url)),EL=uu.resolve(GT,"..","..","client","dist"),_L="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",IL=new Map([..._L].map((e,t)=>[e,t]));function RL(e){let t=[],n=0,r=0;for(let o of e){let s=IL.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 $L(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=RL(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 OL(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 KT=new Map;async function CL(e){let t;try{t=await qT.stat(e)}catch{return null}let n=KT.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await qT.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:$L(r.mappings||"")};return KT.set(e,o),o}catch{return null}}var Ih=null;function ML(){return Ih||(Ih=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):AL("git",["rev-parse","--short","HEAD"],{cwd:uu.resolve(GT,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Ih}var NL=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function PL(e,{distDir:t=EL}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(NL)){let[a,i,c,l]=s,u=await CL(uu.join(t,"assets",`${i}.map`)),f=u?OL(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function VT(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await ML();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await PL(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var BL=()=>process.env.BOSTRAT_STATE_DIR||Ms.join(process.env.HOME||YT.tmpdir(),".bostrat"),vr=()=>Ms.join(BL(),"telemetry"),XT=()=>Ms.join(vr(),".relay-offsets.json"),DL=50,jL=14,LL=3e4,zT=500,pt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function FL(e,t){if(!e||typeof e!="object")return null;let n=pt(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:pt(e.anonId,64),sessionId:pt(e.sessionId,64),userId:pt(e.userId,64),release:pt(e.release,64),appVersion:pt(e.appVersion,32),route:pt(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:pt(e.error.kind,256),message:pt(e.error.message,8192),stack:pt(e.error.stack,32768),fingerprint:pt(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:pt(s?.n,64),r:pt(s?.r,128)}))),o}var ZT=e=>new Date(e).toISOString().slice(0,10),JT=0;async function QT(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,DL):[]).map(s=>FL(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await VT(s);await Rn.mkdir(vr(),{recursive:!0});let o=Ms.join(vr(),`${ZT(t)}.ndjson`);return await Rn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
587
|
-
`).join(""),"utf8"),t-
|
|
588
|
-
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=zT){let m=f.slice(d,d+zT).map(w=>HL(w)),p=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:m})}).catch(()=>null);if(!p||!p.ok)return{sent:o,disabled:!1};o+=m.length}r[s]=i+l+1,await nA(r)}return{sent:o,disabled:!1}}var Rh=null,$h=!1;function rA(){Rh||(Rh=setInterval(async()=>{if(!$h){$h=!0;try{await WL()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{$h=!1}}},LL),Rh.unref?.())}import pu from"node:fs/promises";import cA from"node:path";import qL from"node:os";var lA=()=>process.env.BOSTRAT_STATE_DIR||cA.join(process.env.HOME||qL.tmpdir(),".bostrat"),uA=()=>cA.join(lA(),"dictation-glossary.json"),KL=100,GL=500,VL=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],mu=null,Oh=null,Ch=Promise.resolve();function oA(){let e={},t=Date.now();for(let n of VL)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function dA(){return mu||(Oh??=pu.readFile(uA(),"utf8").then(e=>JSON.parse(e)?.terms??oA()).catch(()=>oA()).then(e=>mu=e).finally(()=>Oh=null),Oh)}async function sA(){let e=JSON.stringify({terms:mu},null,2),t=uA(),n=`${t}.${process.pid}.tmp`;await pu.mkdir(lA(),{recursive:!0}),await pu.writeFile(n,e),await pu.rename(n,t)}function zL(){return Ch=Ch.then(sA,sA),Ch.catch(()=>{})}async function Mh(){return Object.values(await dA()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var JL=e=>Math.max(1,Math.ceil(e.length/4));async function YL(){let e=[],t=0;for(let{term:n}of await Mh()){let r=JL(n);if(!(t+r>GL)&&(e.push(n),t+=r,e.length>=KL))break}return e}async function XL(e,t){let n=await dA(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var aA=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),fu=e=>e.toLowerCase();function iA(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function fA(e,t){let n=aA(e),r=aA(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let f=s-1;f>=0;f--)a[u][f]=fu(n[u])===fu(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)fu(n[c])===fu(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(iA(r[l])&&i.add(r[l]),l++);for(;l<s;l++)iA(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await XL(u,"learned");return await zL(),[...i]}var Si=null;function ZL(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 pA(){if(!Si)return null;let e=await ci();if(!e||e.apiKey.slice(-6)!==Si.keyTail)return null;let{keyTail:t,...n}=Si;return n}async function mA(e,t){let n=await ci();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 YL())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=ZL(o.status,i);c.persistent&&(Si={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 Si=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}le();var Nh={"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 QL(e){if(!e)return null;let t=String(e).trim();if(Nh[t])return t;let n=null;for(let r of Object.keys(Nh))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function eF(e,t={}){let n=Nh[QL(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 tF(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 Ti=null;async function nF(){if(!Ti)return null;let e=await Kl();if(!e||e.apiKey.slice(-6)!==Ti.keyTail)return null;let{keyTail:t,...n}=Ti;return n}async function rF(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=tF(t.status,o);s.persistent&&(Ti={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}Ti=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 oF={openai:rF};async function hA(){let e=await Kl();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await nF()}:{configured:!1}}async function wA(){let e=await Kl();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 oF[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function gA(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=eF(t,s),i=await I(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var SA=pe.dirname(aF(import.meta.url)),yA=Number(process.env.PORT)||3362,wu=Cm(process.env),TA=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Ns=fT(process.env.BOSTRAT_ALLOW_IPS),Lh=pT(process.env.BOSTRAT_VIEW_FRONT_CIDRS,iu),AA=hT(),EA=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=vt();x.disable("x-powered-by");x.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),au(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});x.use((e,t,n)=>{if(bi(e)||Ns.allowed(Ns.clientIp(e))||Lh.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(kT);x.use(yT({token:AA,enforce:EA}));x.use("/api/mocks",vt.json({limit:"1mb"}));x.use("/api/mocks",vt.raw({type:"image/png",limit:"24mb"}));x.use("/api/mocks",vt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));x.use("/api/runbooks",vt.json({limit:"1mb"}));x.use("/api/tracks/adopt",vt.json({limit:"1mb"}));x.use("/api/telemetry",vt.json({limit:"256kb"}));x.use("/api/hook",vt.json({limit:"2mb"}));x.use("/api/dictation",vt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",vt.raw({type:()=>!0,limit:"25mb"}));x.use(vt.json({limit:"64kb"}));x.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:ct,hostTmuxAlive:await jn(),autoTriggerPct:TA})});x.get("/api/sessions",async(e,t)=>{t.json({sessions:await Tt()})});x.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 Ln(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})}});x.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ae(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 Dt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ae(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 Qs(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});x.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ae(n))return t.status(400).json({error:"invalid session name"});try{let r=await Oy(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)})}});x.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=Sc(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 bA=10;x.get("/api/idle-guard",async(e,t)=>{try{t.json(Om({tracks:await oe(),views:await ll(),queuedOf:n=>to(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Qt=oo(),dF=Jv({kind:Qt}),Ai=null,Ct=()=>Ai&&["auto","prompt","off"].includes(Ai)?Ai:dF,hu=null;async function fF(e){let t=Ct();Ai=e??null;let n=Ct();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Ei.cancel();return}hu&&!ii(await ks())&&Ei.arm({to:hu.latest,from:hu.current,kind:Qt})}}var pF=(()=>{try{return uF(import.meta.url)("../package.json").version||null}catch{return null}})(),Ei=Xv({isBusy:async()=>Om({tracks:await oe(),views:await ll(),queuedOf:e=>to(e)}).busy,onStuck:({to:e,from:t})=>ur({current:t,latest:e,installKind:Qt,policy:Ct(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?ur({current:t,latest:e,installKind:Qt,policy:Ct(),status:"updating",reason:"auto"}):ur({current:t,latest:e,installKind:Qt,policy:Ct(),status:"failed",error:n?.reason})).catch(()=>{})});x.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Qt,policy:Ct(),policySource:Ai?"plane":"node",current:pF,status:await ks(),armed:Ei.armed})});x.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 Bm({to:n,kind:Qt,reason:"manual"});if(r.ok)return Ei.cancel(),ur({current:r.from,latest:n,installKind:Qt,policy:Ct(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function _A(){try{let e=await Yv();e&&await ur({current:e.from,latest:e.to,installKind:e.kind||Qt,policy:Ct(),status:e.status,error:e.error,reason:e.reason})}catch{}}_A();setInterval(_A,3e4).unref();x.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>bA){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,bA).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Za,agentAvailability:await vl(),tickets:await kS()})});x.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 VS(n);if(r.cursorUsage=Qf(),e.query.format==="text")return t.type("text/plain").send(zS(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/roundup",async(e,t)=>{try{t.json(await XS(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)})}});x.get("/api/usage/spend",async(e,t)=>{try{let n=await nT(await oe(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Qf(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=ba(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(e,t)=>{t.json(ba())});x.post("/api/usage/caps",async(e,t)=>{try{t.json(await Mc(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});x.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await iT(await oe(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Wh(e.get(Oi),{byToken:Qp,legacy:()=>Oa(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Bs("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Bs("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&&Bs("mismatch",{track:r.track.id,box:r.box.name}),Bs(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 HS(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&Ki(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});x.post("/api/telemetry",async(e,t)=>{try{if(!eA(Ns.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await QT(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});x.get("/api/repos",async(e,t)=>{try{let n=await en(),r=new Set((await Promise.all(n.map(a=>de(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Us()).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)})}});x.get("/api/github/token",async(e,t)=>{try{t.json(await Di(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/github/status",(e,t)=>{t.json({configured:Bi(),calls:Bk(),latency:Fk()})});x.post("/api/github/refresh",(e,t)=>{Qh(),t.json({ok:!0})});x.get("/api/gmail/search",async(e,t)=>{try{let n=await lT(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)})}});x.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await uT(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Fh(e,t,n){let r=e.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>t&&o?.base===t||n&&o?.repo===n):!r?.base||!t||r.base===t||n&&r.repo===n}async function gu({repo:e=null,number:t=null,branch:n=null}){let r=e?await de(e):null;if(r&&t!=null){let s=await Ze(r,t),a=s&&await te(s.trackId);if(a)return a}if(!n)return null;let o=await oe();return Qc(o,n,s=>Fh(s,e,r))}async function IA({repo:e=null,number:t=null,branch:n=null}){let r=await gu({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=e?await de(e).catch(()=>null):null,s=await oe();return Qc(s,n,a=>!a.archivedAt&&!a.transferredTo&&Fh(a,e,o))}async function Mt(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await gu({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await de(s).catch(()=>null):null);await I({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function mF(e){let t=(await oe()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of e??[]){if(!n.prs?.length)continue;let r=await de(n.path);if(r)for(let o of n.prs){if(await Ze(r,o.number))continue;let s=await Qc(t,o.headRefName,a=>Fh(a,n.path,r));s&&await ut({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}x.get("/api/git/prs",async(e,t)=>{try{let n=await Vk();await mF(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=e.body||{};try{if(s){let w=o||"merge";if(!Ea.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let b=await de((await dn(n)).path);if(!b)throw new Error("repo has no GitHub remote");let v=await gu({repo:n,number:y,branch:l}).catch(()=>null);if(!v)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await _a(n,y),await ut({repo:b,number:y,trackId:v.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await xa(b,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await qr(b,y,i===!0),await va(b,y,c===!0&&i!==!0),Ut(),await Mt("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:v,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}let d=await Qo(n,r,{method:o||"squash"});await Mt("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let m=await de(n).catch(()=>null),p=!1;if(m&&(i===!0&&await qr(m,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await va(m,d.number,!0).catch(()=>{}),await pn(m,d.number).catch(()=>{}),p=await fn(m,d.number).catch(()=>!1),await Zn(m,d.number,"merged").catch(()=>{})),i===!0&&!p){let w=await gu({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await no(w.id).catch(()=>{})}t.json(d)}catch(d){let m=d?.message||String(d),p=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(m)?400:502;t.status(p).json({error:m})}});x.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await de((await dn(n)).path).catch(()=>null),l=c?await Sa(c,i).catch(()=>null):null;try{await Bf(n,i)}catch(u){if(!l)throw u}Ut(),await Mt("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})}});x.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await de(n).catch(()=>null),s=await Xk(n,{method:r||"merge",onMerged:a=>{Mt("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&pn(o,a.number).catch(()=>{}).then(()=>fn(o,a.number)).catch(()=>{}).then(()=>Zn(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})}});x.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:Yk()})});x.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(!Ea.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await te(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Kr()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await qr(d.repo,d.number,o===!0).catch(()=>{}),await va(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let m={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(m);try{let p=await Vr(d.repo);if(!p)throw new Error(`no local checkout for ${d.repo}`);let w=await Zo(p,d.number);f.push({r:m,l:d,repoPath:p,pr:w,plan:zk(w,{update:r!==!1})})}catch(p){m.ok=!1,m.action="failed",m.reason=p?.message||String(p)}}for(let{r:d,l:m,repoPath:p,pr:w,plan:y}of Jk(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Qo(p,m.number,{method:i}),await Mt("merged",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:p,track:c}),await pn(m.repo,m.number).catch(()=>{}),await fn(m.repo,m.number).catch(()=>{}),await Zn(m.repo,m.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await _a(p,m.number).catch(()=>{}),d.action="updated-armed"),!await xa(m.repo,m.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Mt("auto-armed",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:p,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(b){d.ok=!1,d.action="failed",d.reason=b?.message||String(b)}if(Ut(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await qr(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await va(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});x.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 te(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Kr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Sa(a.repo,a.number).catch(()=>null),c=await Vr(a.repo).catch(()=>null);c&&await Bf(c,a.number).catch(()=>{}),i&&(await Mt("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}))}Ut(),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})}});x.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 te(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=ft(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let y=u.find(b=>b.name===w||b.repo===w||b.base===w||b.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(f=await os(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let m=[];for(let w of f)try{let y=await jf(w.dir,{title:r,body:o,base:s}),b=w.repo||await de(w.dir).catch(()=>null);b&&y.number&&await ut({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Mt("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??w.name}}),m.push({repo:w.name,slug:b??null,ok:!0,url:y.url,number:y.number})}catch(y){m.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let p=m.filter(w=>w.ok);return p.length?t.json({ok:!0,multi:!0,results:m,url:p[0].url,number:p[0].number}):t.status(502).json({error:m.map(w=>`${w.repo}: ${w.error}`).join("; "),results:m})}let c=await jf(i.cwd,{title:r,body:o,base:s}),l=await de(i.cwd).catch(()=>null);l&&c.number&&await ut({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Mt("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})}});x.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await os(n),o=new Map(r.map(a=>[a.base,a])),s=ft(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)})}});x.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 um(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})}});x.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await dm(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})}});x.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Zo(n,r),s=await de(n).catch(()=>null),a=await IA({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=ft(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await Zk(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?pe.join("ci",i.name):"ci",f=pe.join(a.cwd,".bostrat",u),d=pe.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ve.promises.mkdir(f,{recursive:!0}),await Ve.promises.writeFile(pe.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
589
|
-
${
|
|
587
|
+
`),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 FT(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}.${Jj(n.host)}/`}function Jj(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 hu(t)&&xp(n[0])?n.slice(1).join("."):t}us();Np();Al();np();ue();import UT from"node:os";import{randomUUID as Yj}from"node:crypto";var Xj=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||UT.hostname(),nodeName:e.BOSTRAT_NODE_NAME||UT.hostname()});var HT=32768,WT=120,qT=1e3,KT=new Set(["claude","codex","cursor"]),Zj=new Set(["off","transition","nag"]),Qj=[[0,59],[0,23],[1,31],[1,12],[0,6]];function eL(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=Qj[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 Dn=e=>String(e??"").trim();function tL(e,t){let n=e&&typeof e=="object"?e:{},r=Dn(n.name);if(!r)throw new Error("a loop plan needs a name");if(r.length>WT)throw new Error(`name is too long (${r.length}); cap is ${WT}`);let o=Dn(n.schedule);if(!o)throw new Error('a loop plan needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!eL(o))throw new Error(`invalid cron schedule: "${o}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);let s=Dn(n.prompt);if(!s)throw new Error("a loop plan needs a prompt \u2014 it is what the run does");if(s.length>HT)throw new Error(`prompt is too long (${s.length} chars); cap is ${HT}`);let a=Dn(n.expectation)||null;if(a&&a.length>qT)throw new Error(`expectation is too long (${a.length}); cap is ${qT}`);let i=Dn(n.notify)||"off";if(!Zj.has(i))throw new Error(`notify must be one of off | transition | nag, got "${n.notify}"`);let c=n.assignment??null,l=Dn(typeof c=="string"?c:c?.provider)||"claude";if(!KT.has(l))throw new Error(`unknown provider "${l}" \u2014 one of ${[...KT].join(", ")}`);let u=typeof c=="object"&&c&&Dn(c.model)||null,f=Dn(t?.workspace?.repo)||Dn(t?.workspace?.base)||null;if(!f)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:d,nodeName:p}=Xj();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:u?{provider:l,model:u}:{provider:l},repo:f,nodeId:d||null,nodeName:p||null}}async function GT(e,t){let n=tL(t,e),r=Yj();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 VT(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)}}Kn();ue();wn();Al();var zT=10,JT=200,nL=120,rL=24,XT=/^\d{4}-\d{2}-\d{2}$/;function Ch(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var oL=(e=Date.now())=>new Date(e).toISOString().slice(0,10),Je=(e,t="i")=>new RegExp(e,t),sL=[{re:Je("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:Je("\\bSPC\\b",""),reason:"business (SPC)"},{re:Je("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:Je("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:Je("\\bReplicas\\b",""),reason:"competitor research"},{re:Je("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:Je("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:Je("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],aL=[Je("\\bSPC\\b",""),Je("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),Je("competitor|cloudcli|\\bkiro\\b"),Je("\\bReplicas\\b",""),Je("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),Je("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),Je("(^|[^\\w&])#\\d+\\b"),Je("\\btrack/[\\w.-]+")];function iL({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of sL)if(o.test(n)||o.test(r))return s;return null}function YT(e){let t=String(e||"");for(let n of aL){let r=n.exec(t);if(r)return r[0].trim()}return null}function ZT(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 cL=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function lL(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=za(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(`
|
|
588
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,nL)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${rL}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=cL.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let f=YT(i);if(f)throw new Error(`bullet ${r.length+1} mentions "${f}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>JT)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${JT}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=YT(n);if(o)throw new Error(`headline mentions "${o}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items; rewrite it and re-publish.`)}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>zT)throw new Error(`${r.length} bullets \u2014 over the ${zT}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function uL(e,{repos:t=Ch()}={}){let n=new Set(t);return(await Xr()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:iL({title:r.title,headRef:r.headRef})}))}async function QT(e,t){let n=await uL(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function eA(e,{md:t,day:n=null}){let r=lL(t),o=n==null||n===""?oL():String(n);if(!XT.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 tA(e,{day:t}){let n=String(t||"");if(!XT.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await _({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}ns();wn();ss();ue();ns();Qe();fr();wn();Ca();nl();Ba();import{execFile as dL}from"node:child_process";import{promisify as fL}from"node:util";var Nh=fL(dL),nA=3e4,pL=30*6e4,mL=nA,hL=10*6e4,wL=10080*6e4,gL=60*6e4,yL=8,bL=1440*6e4,kL=3,rA="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Mh=60*6e4,xL=15*6e4,SL=9e4,vL=1440*6e4,gu=new Map,yu=new Map,TL=/could not resolve|not found|no pull requests?/i,AL=e=>TL.test(String(e?.message||e||""));function EL(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Mh)}var oA={fast:mL,slow:hL,decay:gL};function sA(e,t){return e.armedAt&&t-e.armedAt<pL||EL(e,t)||aA(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>wL?"decay":"slow"}function _L(e,t){return oA[sA(e,t)]}function Oi(e,t,n,r){let o=e.lanes?.get(Q(t.repo,t.number))||{};return tl({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function IL(e,t){yn("watch");try{let{stdout:n}=await Nh("gh",["pr","view",String(t),"--repo",e,"--json",rA],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await qt({slug:e})}});return JSON.parse(n||"null")}catch(n){throw lr(n)&&ur(n),n}}async function RL(e){yn("watch");try{let{stdout:t}=await Nh("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",rA],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await qt({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw lr(t)&&ur(t),t}}async function $L(){let e=new Map;for(let t of await ae().catch(()=>[]))e.set(t.id,t);return e}async function OL(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 CL(e,t){let n=await Qr(e);return n?es(n,t):null}async function ML(e,t,n){let r=await Qr(e);if(!r)throw new Error(`no local checkout for ${e}`);return ts(r,t,{method:n})}async function NL(e,t){let n=await Qr(e);if(!n)throw new Error(`no local checkout for ${e}`);return Pa(n,t)}async function PL(e,t){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function Os(e,t,n,r,o){if(!await r.disarm(e.repo,e.number).catch(()=>null))return;let a=n.get(e.trackId);a&&await r.emitDisarmed(a,{number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo,reason:o}).catch(()=>{})}var DL=10*6e4;async function BL(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>bL){await Os(e,null,t,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(e.repo,e.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await Os(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Os(e,o,t,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(e.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<DL)return;await Os(e,o,t,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(e.repo,e.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(e.repo,e.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await Os(e,o,t,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(e.repo,e.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await Os(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await iA(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Cs(e,t,n){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function jL(e,t){try{return await Nh("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function $i(e,t,n,r=()=>!0){return(await L(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Ms=(e,t)=>({number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo});function aA(e,t,{grace:n=SL}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=vL}async function LL(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(f=>f.name===s.leadBox);return await(r.midTurn??eo)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await L(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??hn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??hn)(e.repo,e.number).catch(()=>null)?(await Cs(s,"opened",{...Ms(e,t),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${e.repo}#${e.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function FL(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 Vc(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Jc(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<xL;await Fk(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Lk(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await $i(a,"pushed",e.number,f=>f.payload?.sha===s)&&await Cs(a,"pushed",{...Ms(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Mh)await zc(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 zc(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Oi(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await $i(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Cs(a,"ci-fixed",{...Ms(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await zc(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Mh)await Yc(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Yc(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Oi(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await $i(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Cs(a,"reconciled",{...Ms(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Yc(e.repo,e.number).catch(()=>{})}}}}async function UL(e,t,n,r){let o=Oi(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await $i(s,"closed",e.number)&&await Cs(s,"closed",{...Ms(e,t),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(e.repo,e.number,"closed").catch(()=>{}),await r.mark(e.repo,e.number,"closed").catch(()=>{})}async function HL(e,t,n){Oi(n,e,"lost",null);let r=t.get(e.trackId);r&&!await $i(r,"lost",e.number)&&await Cs(r,"lost",Ms(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function iA(e,t,n,r,o={}){let s=Oi(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await L(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function WL(e={}){let{now:t=Date.now(),auth:n=Hf,view:r=IL,list:o=RL,links:s=Wk,trackMap:a=$L,emitMerged:i=OL,mark:c=ir,archiveIfArmed:l=kn,notifyIfArmed:u=xn,notifyIfResolved:f=Dl,batchThreshold:d=kL,detail:p=CL,merge:m=ML,updateBranch:h=NL,lookup:g=qe,disarm:x=Oa,emitDisarmed:k=PL,localHasCommit:A=jL,claim:R=hn,midTurn:M=eo}=e;if(!(await n().catch(()=>null))?.authenticated||Qc()>0)return 0;let T=(await s().catch(()=>[])).filter(F=>t-(gu.get(Q(F.repo,F.number))||0)>=_L(F,t));if(!T.length)return 0;let j=Date.now(),Z=new Map,fe=new Map;for(let F of T){let pe=sA(F,t);Z.set(Q(F.repo,F.number),{lane:pe,intervalMs:oA[pe]}),gu.set(Q(F.repo,F.number),t),fe.has(F.repo)||fe.set(F.repo,[]),fe.get(F.repo).push(F)}let ie=await a().catch(()=>new Map),D={now:t,lanes:Z,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:h,lookup:g,disarm:x,emitDisarmed:k,localHasCommit:A,claim:R,midTurn:M};for(let[F,pe]of fe){let Se,He=new Set;if(pe.length>d)Se=await o(F).catch(()=>null);else{Se=new Map;for(let le of pe)try{let W=await r(F,le.number);W?.number!=null&&Se.set(Number(W.number),W)}catch(W){AL(W)&&He.add(Q(le.repo,le.number))}}if(Se)for(let le of pe){let W=Q(le.repo,le.number),J=Se.get(Number(le.number));if(!J){if(He.has(W)){let ee=(yu.get(W)||0)+1;yu.set(W,ee),ee>=yL&&(await HL(le,ie,D),yu.delete(W),gu.delete(W))}continue}yu.delete(W),J.state==="MERGED"||J.state==="CLOSED"?(J.state==="MERGED"?await iA(le,J,ie,D):await UL(le,J,ie,D),gu.delete(W)):(await LL(le,J,ie,D).catch(()=>{}),await FL(le,J,ie,D).catch(()=>{}),le.autoMerge&&await BL(le,ie,D).catch(()=>{}))}}let I={};for(let{lane:F}of Z.values())I[F]=(I[F]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries(I).map(([F,pe])=>`${F} ${pe}`).join(", ")}) in ${Date.now()-j}ms`),T.length}function cA(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await WL()}catch{}finally{e=!1}}},nA)}ue();wn();Un();fr();function lA(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function qL(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var KL=new Set(["merged","closed","lost"]);function GL(e,t){if(!e?.blocked)return null;let n=lA(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 VL(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function zL(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 JL(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 uA(e,t={}){let{now:n=Date.now(),slugOf:r=me,lookup:o=qe,note:s=Uk,tracks:a=async()=>(await ae()).filter(f=>!f.archivedAt&&!f.transferredTo),asked:i=zL,emitChange:c=JL}=t,l=[],u=null;for(let f of e??[]){if(!f?.prs?.length)continue;let d=await r(f.path).catch(()=>null);if(d)for(let p of f.prs)try{if(!qL(p))continue;let m=await o(d,p.number).catch(()=>null);if(!m?.trackId||KL.has(m.state))continue;let{prev:h}=await s(d,p.number,{blocked:lA(p),sha:p.headSha??null,at:n});if(!h)continue;let g=GL(h,p);if(!g||VL(m,g.change))continue;u??=new Map((await a()).map(R=>[R.id,R]));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:d,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}tt();ue();fr();import{randomUUID as YL}from"node:crypto";var XL=2e4,dA=3,Ns=new Map,Ph=new Set;async function fA(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 ZL(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 $t(),i=n??await ae()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||u.transferredTo||!u.leadBox)continue;let f=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let p=(Ns.get(f.name)||0)+1;Ns.set(f.name,p),p===dA&&(Ph.add(f.name),await fA(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:YL(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(Ns.get(f.name)||0)>=dA;if(Ns.delete(f.name),p||!Ph.has(f.name)){Ph.add(f.name);let m=await fA(u,f.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Ns.keys()])l.has(u)||Ns.delete(u)}var Ci=null,Dh=!1;function pA(){return Ci?()=>{}:(Ci=setInterval(()=>{Dh||(Dh=!0,ZL().catch(()=>{}).finally(()=>{Dh=!1}))},XL),Ci.unref?.(),()=>{clearInterval(Ci),Ci=null})}Yo();Tp();ue();ns();ss();Yo();import{randomUUID as QL}from"node:crypto";async function eF(){let e=await Promise.resolve().then(()=>(fr(),ov));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Bh={listRepoPrs:Vt,readEvents:L,appendEvent:_,sayDeps:eF,currentBranch:rs};async function tF(e,t=Bh,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??rs)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function jh(e,t,{actions:n,notify:r=!1}={},o=Bh){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 tF(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=QL();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 Lh(e,{offerId:t,choice:n}={},r=Bh){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=zo.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Jo(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,Rk(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import Bn from"node:fs/promises";import Ps from"node:path";import kA from"node:os";import mA from"node:fs/promises";import bu from"node:path";import{execFile as nF}from"node:child_process";import{promisify as rF}from"node:util";import{fileURLToPath as oF}from"node:url";var sF=rF(nF),wA=bu.dirname(oF(import.meta.url)),aF=bu.resolve(wA,"..","..","client","dist"),iF="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",cF=new Map([...iF].map((e,t)=>[e,t]));function lF(e){let t=[],n=0,r=0;for(let o of e){let s=cF.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 uF(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=lF(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 dF(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 hA=new Map;async function fF(e){let t;try{t=await mA.stat(e)}catch{return null}let n=hA.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:uF(r.mappings||"")};return hA.set(e,o),o}catch{return null}}var Fh=null;function pF(){return Fh||(Fh=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):sF("git",["rev-parse","--short","HEAD"],{cwd:bu.resolve(wA,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Fh}var mF=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function hF(e,{distDir:t=aF}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(mF)){let[a,i,c,l]=s,u=await fF(bu.join(t,"assets",`${i}.map`)),f=u?dF(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function gA(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await pF();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await hF(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var wF=()=>process.env.BOSTRAT_STATE_DIR||Ps.join(process.env.HOME||kA.tmpdir(),".bostrat"),Rr=()=>Ps.join(wF(),"telemetry"),xA=()=>Ps.join(Rr(),".relay-offsets.json"),gF=50,yF=14,bF=3e4,yA=500,yt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function kF(e,t){if(!e||typeof e!="object")return null;let n=yt(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:yt(e.anonId,64),sessionId:yt(e.sessionId,64),userId:yt(e.userId,64),release:yt(e.release,64),appVersion:yt(e.appVersion,32),route:yt(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:yt(e.error.kind,256),message:yt(e.error.message,8192),stack:yt(e.error.stack,32768),fingerprint:yt(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:yt(s?.n,64),r:yt(s?.r,128)}))),o}var SA=e=>new Date(e).toISOString().slice(0,10),bA=0;async function vA(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,gF):[]).map(s=>kF(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await gA(s);await Bn.mkdir(Rr(),{recursive:!0});let o=Ps.join(Rr(),`${SA(t)}.ndjson`);return await Bn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
589
|
+
`).join(""),"utf8"),t-bA>6*3600*1e3&&(bA=t,xF(t).catch(()=>{})),r.length}async function xF(e=Date.now()){let t=SA(e-yF*24*3600*1e3),n=[];try{n=await Bn.readdir(Rr())}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 Bn.rm(Ps.join(Rr(),s),{force:!0});let o=await AA();for(let s of r)delete o[s];await EA(o)}var ku=new Map;function TA(e,t=Date.now()){let n=e||"unknown",r=ku.get(n);return r||(ku.size>512&&ku.clear(),r={tokens:40,at:t},ku.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 AA(){try{return JSON.parse(await Bn.readFile(xA(),"utf8"))||{}}catch{return{}}}async function EA(e){await Bn.mkdir(Rr(),{recursive:!0}),await Bn.writeFile(xA(),JSON.stringify(e),"utf8")}function SF(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||kA.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 vF({fetchImpl:e=fetch}={}){let t=await Tv();if(!t)return{sent:0,disabled:!0};let n;try{n=(await Bn.readdir(Rr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await AA(),o=0;for(let s of n){let a=Ps.join(Rr(),s),i=r[s]||0,c;try{let d=await Bn.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
590
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=yA){let p=f.slice(d,d+yA).map(h=>SF(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 EA(r)}return{sent:o,disabled:!1}}var Uh=null,Hh=!1;function _A(){Uh||(Uh=setInterval(async()=>{if(!Hh){Hh=!0;try{await vF()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Hh=!1}}},bF),Uh.unref?.())}import Su from"node:fs/promises";import CA from"node:path";import TF from"node:os";var MA=()=>process.env.BOSTRAT_STATE_DIR||CA.join(process.env.HOME||TF.tmpdir(),".bostrat"),NA=()=>CA.join(MA(),"dictation-glossary.json"),AF=100,EF=500,_F=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],vu=null,Wh=null,qh=Promise.resolve();function IA(){let e={},t=Date.now();for(let n of _F)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function PA(){return vu||(Wh??=Su.readFile(NA(),"utf8").then(e=>JSON.parse(e)?.terms??IA()).catch(()=>IA()).then(e=>vu=e).finally(()=>Wh=null),Wh)}async function RA(){let e=JSON.stringify({terms:vu},null,2),t=NA(),n=`${t}.${process.pid}.tmp`;await Su.mkdir(MA(),{recursive:!0}),await Su.writeFile(n,e),await Su.rename(n,t)}function IF(){return qh=qh.then(RA,RA),qh.catch(()=>{})}async function Kh(){return Object.values(await PA()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var RF=e=>Math.max(1,Math.ceil(e.length/4));async function $F(){let e=[],t=0;for(let{term:n}of await Kh()){let r=RF(n);if(!(t+r>EF)&&(e.push(n),t+=r,e.length>=AF))break}return e}async function OF(e,t){let n=await PA(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var $A=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),xu=e=>e.toLowerCase();function OA(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function DA(e,t){let n=$A(e),r=$A(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let f=s-1;f>=0;f--)a[u][f]=xu(n[u])===xu(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)xu(n[c])===xu(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(OA(r[l])&&i.add(r[l]),l++);for(;l<s;l++)OA(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await OF(u,"learned");return await IF(),[...i]}var Mi=null;function CF(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 BA(){if(!Mi)return null;let e=await yi();if(!e||e.apiKey.slice(-6)!==Mi.keyTail)return null;let{keyTail:t,...n}=Mi;return n}async function jA(e,t){let n=await yi();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await $F())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=CF(o.status,i);c.persistent&&(Mi={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return Mi=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ue();var Gh={"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 MF(e){if(!e)return null;let t=String(e).trim();if(Gh[t])return t;let n=null;for(let r of Object.keys(Gh))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function NF(e,t={}){let n=Gh[MF(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 PF(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.error?.code||n?.error?.type||"";return e===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${e}): ${t.slice(0,300)}`}}var Ni=null;async function DF(){if(!Ni)return null;let e=await eu();if(!e||e.apiKey.slice(-6)!==Ni.keyTail)return null;let{keyTail:t,...n}=Ni;return n}async function BF(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=PF(t.status,o);s.persistent&&(Ni={code:s.code,message:s.message,at:Date.now(),keyTail:e.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}Ni=null;let n=await t.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var jF={openai:BF};async function LA(){let e=await eu();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await DF()}:{configured:!1}}async function FA(){let e=await eu();if(!e){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let t=await jF[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function UA(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=NF(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 VA=we.dirname(FF(import.meta.url)),HA=Number(process.env.PORT)||3362,Au=Km(process.env),zA=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Ds=$T(process.env.BOSTRAT_ALLOW_IPS),Xh=OT(process.env.BOSTRAT_VIEW_FRONT_CIDRS,wu),JA=MT(),YA=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",S=It();S.disable("x-powered-by");S.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),hu(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});S.use((e,t,n)=>{if(Ri(e)||Ds.allowed(Ds.clientIp(e))||Xh.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(jT);S.use(DT({token:JA,enforce:YA}));S.use("/api/mocks",It.json({limit:"1mb"}));S.use("/api/mocks",It.raw({type:"image/png",limit:"24mb"}));S.use("/api/mocks",It.raw({type:["video/mp4","video/webm"],limit:"105mb"}));S.use("/api/runbooks",It.json({limit:"1mb"}));S.use("/api/tracks/adopt",It.json({limit:"1mb"}));S.use("/api/telemetry",It.json({limit:"256kb"}));S.use("/api/hook",It.json({limit:"2mb"}));S.use("/api/dictation",It.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",It.raw({type:()=>!0,limit:"25mb"}));S.use(It.json({limit:"64kb"}));S.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:ft,hostTmuxAlive:await Vn(),autoTriggerPct:zA})});S.get("/api/sessions",async(e,t)=>{t.json({sessions:await $t()})});S.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await zn(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});S.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});if(!await se(n))return t.status(404).json({error:"session not found"});try{await Ot(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});S.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});let r=await se(n);if(!r)return t.status(404).json({error:"session not found"});let o=await aa(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});S.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});try{let r=await Gy(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=Cc(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 WA=10;S.get("/api/idle-guard",async(e,t)=>{try{t.json(qm({tracks:await ae(),views:await gl(),queuedOf:n=>ao(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Ft=lo(),KF=pv({kind:Ft}),Pi=null,ut=()=>Pi&&["auto","prompt","off"].includes(Pi)?Pi:KF,Tu=null;async function GF(e){let t=ut();Pi=e??null;let n=ut();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Bs.cancel();return}Tu&&!gi(await Ss())&&Bs.arm({to:Tu.latest,from:Tu.current,kind:Ft})}}var VF=(()=>{try{return qF(import.meta.url)("../package.json").version||null}catch{return null}})(),Bs=hv({isBusy:async()=>qm({tracks:await ae(),views:await gl(),queuedOf:e=>ao(e)}).busy,onStuck:({to:e,from:t})=>on({current:t,latest:e,installKind:Ft,policy:ut(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?on({current:t,latest:e,installKind:Ft,policy:ut(),status:"updating",reason:"auto"}):on({current:t,latest:e,installKind:Ft,policy:ut(),status:"failed",error:n?.reason})).catch(()=>{})});S.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Ft,policy:ut(),policySource:Pi?"plane":"node",current:VF,status:await Ss(),armed:Bs.armed})});S.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await zl({to:n,kind:Ft,reason:"manual"});if(r.ok)return Bs.cancel(),on({current:r.from,latest:n,installKind:Ft,policy:ut(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function XA(){try{let e=await mv();e&&await on({current:e.from,latest:e.to,installKind:e.kind||Ft,policy:ut(),status:e.status,error:e.error,reason:e.reason})}catch{}}XA();setInterval(XA,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await ae()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>WA){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,WA).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:ii,agentAvailability:await $l(),tickets:await jv()})});S.get("/api/cost",async(e,t)=>{try{let n=await ae();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await dT(n);if(r.cursorUsage=cp(),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 hT(await ae(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/spend",async(e,t)=>{try{let n=await kT(await ae(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=cp(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=_a(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(_a())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await Uc(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 AT(await ae(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await tw(e.get(Fi),{byToken:um,legacy:()=>La(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ls("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ls("legacy-denied",{reason:"tokenless-denied",hook:e.body?.hook_event_name}),t.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&e.body?.session_id&&r.box?.sessionId&&e.body.session_id!==r.box.sessionId&&Ls("mismatch",{track:r.track.id,box:r.box.name}),Ls(n,{track:r?.track?.id,hook:e.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await aT(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&tc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/telemetry",async(e,t)=>{try{if(!TA(Ds.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await vA(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/repos",async(e,t)=>{try{let n=await cn(),r=new Set((await Promise.all(n.map(a=>me(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await qs()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/github/token",async(e,t)=>{try{t.json(await Gi(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/github/status",(e,t)=>{t.json({configured:Ki(),calls:Qk(),latency:rx()})});S.post("/api/github/refresh",(e,t)=>{dw(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await _T(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 IT(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Zh(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 Eu({repo:e=null,number:t=null,branch:n=null}){let r=e?await me(e):null;if(r&&t!=null){let s=await qe(r,t),a=s&&await ne(s.trackId);if(a)return a}if(!n)return null;let o=await ae();return il(o,n,s=>Zh(s,e,r))}async function ZA({repo:e=null,number:t=null,branch:n=null}){let r=await Eu({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=e?await me(e).catch(()=>null):null,s=await ae();return il(s,n,a=>!a.archivedAt&&!a.transferredTo&&Zh(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 Eu({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 zF(e){let t=(await ae()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of e??[]){if(!n.prs?.length)continue;let r=await me(n.path);if(r)for(let o of n.prs){if(await qe(r,o.number))continue;let s=await il(t,o.headRefName,a=>Zh(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 ux();await zF(n.repos).catch(()=>{}),await uA(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=e.body||{};try{if(s){let h=o||"merge";if(!Na.has(h))throw new Error(`invalid merge method: ${h}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let x=await me((await bn(n)).path);if(!x)throw new Error("repo has no GitHub remote");let k=await Eu({repo:n,number:g,branch:l}).catch(()=>null);if(!k)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Pa(n,g),await mt({repo:x,number:g,trackId:k.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Ra(x,g,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await Yr(x,g,i===!0),await $a(x,g,c===!0&&i!==!0),Jt(),await Ut("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:k,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:g,auto:!0})}let d=await ts(n,r,{method:o||"squash"});await Ut("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await me(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Yr(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await $a(p,d.number,!0).catch(()=>{}),await xn(p,d.number).catch(()=>{}),m=await kn(p,d.number).catch(()=>!1),await ir(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let h=await Eu({repo:n,number:d.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await io(h.id).catch(()=>{})}t.json(d)}catch(d){let p=d?.message||String(d),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(m).json({error:p})}});S.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await me((await bn(n)).path).catch(()=>null),l=c?await Oa(c,i).catch(()=>null):null;try{await Kf(n,i)}catch(u){if(!l)throw u}Jt(),await Ut("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await me(n).catch(()=>null),s=await mx(n,{method:r||"merge",onMerged:a=>{Ut("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&xn(o,a.number).catch(()=>{}).then(()=>kn(o,a.number)).catch(()=>{}).then(()=>ir(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});S.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:px()})});S.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!Na.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await ne(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Xr()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await Yr(d.repo,d.number,o===!0).catch(()=>{}),await $a(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let p={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(p);try{let m=await Qr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let h=await es(m,d.number);f.push({r:p,l:d,repoPath:m,pr:h,plan:dx(h,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:d,l:p,repoPath:m,pr:h,plan:g}of fx(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await ts(m,p.number,{method:i}),await Ut("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await xn(p.repo,p.number).catch(()=>{}),await kn(p.repo,p.number).catch(()=>{}),await ir(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await Pa(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Ra(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Ut("auto-armed",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(x){d.ok=!1,d.action="failed",d.reason=x?.message||String(x)}if(Jt(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Yr(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await $a(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await ne(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Xr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Oa(a.repo,a.number).catch(()=>null),c=await Qr(a.repo).catch(()=>null);c&&await Kf(c,a.number).catch(()=>{}),i&&(await Ut("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}Jt(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await ne(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=gt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(h=>{let g=u.find(x=>x.name===h||x.repo===h||x.base===h||x.dir===h);if(!g)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return g});else if(f=await as(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of f)try{let g=await Vf(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 Vf(i.cwd,{title:r,body:o,base:s}),l=await me(i.cwd).catch(()=>null);l&&c.number&&await mt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ut("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});S.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await as(n),o=new Map(r.map(a=>[a.base,a])),s=gt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await xm(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 Sm(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});S.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await es(n,r),s=await me(n).catch(()=>null),a=await ZA({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 hx(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?we.join("ci",i.name):"ci",f=we.join(a.cwd,".bostrat",u),d=we.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ye.promises.mkdir(f,{recursive:!0}),await Ye.promises.writeFile(we.join(a.cwd,d),l.map(x=>`\u2550\u2550\u2550\u2550 ${x.name}${x.link?` \xB7 ${x.link}`:""} \u2550\u2550\u2550\u2550
|
|
591
|
+
${x.log??"(no fetchable log \u2014 see link)"}
|
|
590
592
|
`).join(`
|
|
591
|
-
`));let m=l.map(b=>b.name).join(", "),p=c&&i?` in the ${i.name}/ checkout`:"",w=await xt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${p}. Failing check${l.length===1?"":"s"}: ${m}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await ut({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await kk(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Mt("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(b=>b.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(b=>b.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});x.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Zo(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await de(n).catch(()=>null),a=await IA({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=ft(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await xt(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 ut({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await xk(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Mt("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})}});x.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=Go.filter(f=>e.body.actions.includes(f)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await B(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await Eh(a,c,{actions:o,notify:s});if(f)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await xt(a.id,Vo(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await I({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&sm(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})}});x.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await _h(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 _i=e=>t=>({...t,url:vT(t,e.headers)});x.get("/api/views",async(e,t)=>{try{let n=await ll({trackId:e.query.track||void 0});t.json({views:n.map(_i(e)),maxLive:sr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(e,t)=>{let n=await V0(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:_i(e)(n),session:ss(n.slug)})});x.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await te(n):null;if(!i&&s&&(i=(await oe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+pe.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await pp({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:_i(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})}});x.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:_i(e)(await z0(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:_i(e)(await J0(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})}});x.delete("/api/views/:id",async(e,t)=>{try{t.json(await Y0(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var hF=[["linear",so,qm],["jira",ql,Hm]],Ph=new Map;x.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Um(),r={};for(let[o,s,a]of hF){let i=await s();if(!i){Ph.delete(o),r[o]={configured:!1};continue}let c=Ph.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},Ph.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)})}});x.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await Um()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await sS(o),i=n?await te(n):null;!i&&r&&(i=(await oe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+pe.sep)))||null),i&&await I({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var uo=async(e,t)=>{let n=e?await te(e):null;return!n&&t&&(n=(await oe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+pe.sep)))||null),n};x.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Dm()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});x.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await uo(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 xs(o);await Fl(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})}});x.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await uo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Fl(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var RA=async(e,t,n)=>{if(e)return{ref:e,track:await uo(t,n)};let r=await uo(t,n),o=r?await Lm(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}};x.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await RA(o,n,r),l=await Zv({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})}});x.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await RA(e.query.circuit,e.query.track,e.query.cwd);t.json(await jm(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})}});x.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,alternatives:i,supersedesId:c}=e.body||{};try{let l=await uo(n,r);if(!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=await eS(l,{title:o,context:s,decision:a,alternatives:i,supersedesId:c});t.json({ok:!0,trackId:l.id,...u})}catch(l){let u=l?.message||String(l);t.status(/required/i.test(u)?400:502).json({error:u})}});x.get("/api/decisions",async(e,t)=>{try{let n=await uo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await tS(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})}});x.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 uo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Lm(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 Qv(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})}});x.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await oe()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+pe.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 Ts(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})}});x.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await te(r):null;if(!o&&n&&(o=(await oe()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+pe.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 xS(),ticket:await Zt(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});x.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Zt(e.params.id)})});x.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ts(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})}});x.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await nh(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;x.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await AS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(bu(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await rh(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(bu(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await ES(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(bu(r)).json({error:r})}});x.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await _S(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(bu(s)).json({error:s})}});x.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await ym(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})}});x.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await oe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+pe.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 RS(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})}});x.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await IS()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});x.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await te(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await $S(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})}});x.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await La({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.get("/api/mocks/:id",async(e,t)=>{let n=await gn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Ii={dotfiles:"allow"};x.get("/api/mocks/:id/png",async(e,t)=>{let n=await gn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Rt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Da(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,Ii,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/media",async(e,t)=>{let n=await gn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Rt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Da(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,Ii,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});x.get("/api/mocks/:id/html",async(e,t)=>{let n=await gn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Rt(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(bt(n,r.v).html,Ii,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});x.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 f=a?await te(a):null;if(!f&&c&&(f=(await oe()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+pe.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(p=>p.name===f.leadBox),m=await dx({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});t.json({ok:!0,mock:m})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});x.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 fx(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})}});x.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await hx(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await xt(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})}});x.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await Mx(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await wx(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})}});x.get("/api/flows",async(e,t)=>{try{t.json({flows:await Ix({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});x.get("/api/flows/:id",async(e,t)=>{let n=await Rx(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});x.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await te(n):null;if(!a&&s&&(a=(await oe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+pe.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 $x({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})}});x.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await Ox(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})}});x.delete("/api/flows/: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})}});x.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+pe.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await Ux({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)})}});x.get("/api/runbooks/:id",async(e,t)=>{let n=await _p(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});x.get("/api/runbooks/:id/md",async(e,t)=>{let n=await _p(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=Lx(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 Hx(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(xn(n,r.v),Ii,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});x.get("/api/loops",async(e,t)=>{t.json(await nl())});x.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+pe.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await nl();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 Gf({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})});x.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await oe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+pe.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 _T(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});x.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 te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await IT(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})}});x.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await te(n):null;if(!a&&s&&(a=(await oe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+pe.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 Wx({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})}});x.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await qx(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})}});x.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await Kx(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})}});x.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await Gx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/changelog/draft",async(e,t)=>{try{let n=OT(e.query.since||null);t.json({since:n,repos:kh(),prs:await CT(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});x.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=o?await te(o):null;if(!a&&s&&(a=(await oe()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+pe.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await MT(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var wF=120,Bh=400,yu=null;x.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>Bh)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Bh} cap; a notification is a nudge, put the detail in chat`});let c=Fa(`${n||""}
|
|
592
|
-
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await
|
|
593
|
+
`));let p=l.map(x=>x.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await _t(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await mt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Dk(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ut("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(x=>x.name)}}),t.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(x=>x.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await es(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await me(n).catch(()=>null),a=await ZA({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=gt(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await _t(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 Bk(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ut("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=zo.filter(f=>e.body.actions.includes(f)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await ne(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await L(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await jh(a,c,{actions:o,notify:s});if(f)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await _t(a.id,Jo(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await _({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&wm(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 Lh(o,{offerId:n,choice:r});t.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});var Di=e=>t=>({...t,url:FT(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await gl({trackId:e.query.track||void 0});t.json({views:n.map(Di(e)),maxLive:mr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await u0(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Di(e)(n),session:is(n.slug)})});S.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await ne(n):null;if(!i&&s&&(i=(await ae()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+we.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Sp({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});t.json({ok:!0,view:Di(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});S.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Di(e)(await d0(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Di(e)(await f0(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 p0(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var JF=[["linear",uo,nh],["jira",Ql,eh]],Vh=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Qm(),r={};for(let[o,s,a]of JF){let i=await s();if(!i){Vh.delete(o),r[o]={configured:!1};continue}let c=Vh.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},Vh.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 Qm()})}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 vv(o),i=n?await ne(n):null;!i&&r&&(i=(await ae()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+we.sep)))||null),i&&await _({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var wo=async(e,t)=>{let n=e?await ne(e):null;return!n&&t&&(n=(await ae()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+we.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Jm()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});S.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await wo(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await vs(o);await Jl(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});S.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await wo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Jl(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var QA=async(e,t,n)=>{if(e)return{ref:e,track:await wo(t,n)};let r=await wo(t,n),o=r?await Xm(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 QA(o,n,r),l=await wv({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 QA(e.query.circuit,e.query.track,e.query.cwd);t.json(await Ym(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});S.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,alternatives:i,supersedesId:c}=e.body||{};try{let l=await wo(n,r);if(!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=await yv(l,{title:o,context:s,decision:a,alternatives:i,supersedesId:c});t.json({ok:!0,trackId:l.id,...u})}catch(l){let u=l?.message||String(l);t.status(/required/i.test(u)?400:502).json({error:u})}});S.get("/api/decisions",async(e,t)=>{try{let n=await wo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await bv(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});S.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await wo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Xm(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 gv(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});S.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await ae()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+we.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await Es(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await ne(r):null;if(!o&&n&&(o=(await ae()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+we.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await Lv(),ticket:await an(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});S.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await an(e.params.id)})});S.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Es(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 mh(e.params.id)})});var Iu=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;S.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await Wv(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Iu(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await hh(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Iu(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await qv(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Iu(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 Kv(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Iu(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 Om(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});S.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await ae()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+we.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await Vv(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 Gv()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});S.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await ne(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await zv(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});S.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Va({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/mocks/:id",async(e,t)=>{let n=await An(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Bi={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await An(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Dt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Ka(n,r,"png");if(!o)return t.status(404).json({error:"no image for this version (poster extraction failed)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Bi,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await An(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Dt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Ka(n,r,"video");if(!o)return t.status(404).json({error:"this version has no video (image or html sourced)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Bi,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await An(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Dt(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(Tt(n,r.v).html,Bi,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});S.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&xl(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await ne(a):null;if(!f&&c&&(f=(await ae()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+we.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(m=>m.name===f.leadBox),p=await $0({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});t.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});S.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&xl(n)?n:null;try{let o=await O0(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 N0(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await _t(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 Z0(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await P0(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 V0({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 z0(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});S.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await ne(n):null;if(!a&&s&&(a=(await ae()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+we.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await J0({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 Y0(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 X0(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ae(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+we.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 Dp(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 Dp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=oS(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 iS(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Rn(n,r.v),Bi,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await ul())});S.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ae()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+we.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await ul();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 tp({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});S.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await ae()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+we.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await GT(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 VT(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});S.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await ne(n):null;if(!a&&s&&(a=(await ae()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+we.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await cS({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 lS(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 uS(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 dS(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=ZT(e.query.since||null);t.json({since:n,repos:Ch(),...await QT(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var eE=async({trackId:e,cwd:t})=>{let n=e?await ne(e):null;return!n&&t&&(n=(await ae()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+we.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 eE({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 eA(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 eE({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 tA(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var YF=120,zh=400,_u=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>zh)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${zh} cap; a notification is a nudge, put the detail in chat`});let c=za(`${n||""}
|
|
594
|
+
${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 ae()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+we.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),d={title:n?f(n,YF):l.name,body:f(i,zh),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await _(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),t.json({ok:!0,notification:d,delivered:_u?_u.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 jA(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 yi(),terms:await Kh(),problem:await BA()})});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 DA(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 LA())});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 FA())}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 UA(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 Pl({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),f=null;if(c)try{await Es(u,c,{source:"create"})}catch(p){f=p?.message||String(p)}let d=null;if(l)try{await Jl(u,await vs(l),{source:"picker"})}catch(p){d=p?.message||String(p)}t.json({ok:!0,track:u,...f?{ticketWarning:f}:{},...d?{circuitWarning:d}:{}})}catch(u){let f=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(f)?400:500).json({error:f})}});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:tE(xs(e.params.id)),subtasks:co(e.params.id).map(ji),pendingOffers:await dm(n)})});S.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Em(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await mh(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 io(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 Rm(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 Im(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 Ol(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}
|
|
593
595
|
data: ${JSON.stringify(i)}
|
|
594
596
|
|
|
595
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await
|
|
597
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await L(n))i.seq>o&&r(i);let s=bw(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
596
598
|
|
|
597
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});
|
|
598
|
-
`);
|
|
599
|
+
`),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=ga(await L(n)),a=Ac(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 Dw(),harvest:Cw()})});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 Bw(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 XF=["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(`
|
|
600
|
+
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await _t(e.params.id,XF)})}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 Bl(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 _t(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 gS(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=we.basename(String(e.params.file||""));if(!r||!Lp(we.extname(r).slice(1)))return t.status(400).json({error:"not an upload path"});t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox");let o=jp(n.cwd),s,a;try{s=await Ye.promises.realpath(o),a=await Ye.promises.realpath(we.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+we.sep))return t.status(404).json({error:"upload not found"});t.sendFile(r,{root:o},i=>{i&&!t.headersSent&&t.status(404).json({error:"upload not found"})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});var ZF=/^[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(!ZF.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=Ct(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=LF.createInterface({input:Ye.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let p;try{p=JSON.parse(d)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let f=l?gd(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",f.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(f.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await Nl(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 mm(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 Cl(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 Tm({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 pm(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 bm(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 fu(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await km(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 Um(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:ji(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:co(e.params.id).map(ji)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await fm(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:ji(Hl(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:ji(Hm(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function ji(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 Ll(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 gm(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(tE(xs(e.params.id))??{running:!1})});S.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Pm(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=Nm(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 Fl(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 jm({...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 tE(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:ic(),paths:Zw,trace:await Xw({limit:n})})});var Jh=we.resolve(VA,"..","..","client","dist");Ye.existsSync(Jh)&&(S.use(It.static(Jh)),S.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(we.join(Jh,"index.html"),Bi)));var nE=UF.createServer(S),QF=new WF({noServer:!0}),rE=(e,t,n)=>{if(!(Ri(e)||Ds.allowed(Ds.clientIp(e))||Xh.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
599
601
|
\r
|
|
600
|
-
`),t.destroy();return}if(
|
|
602
|
+
`),t.destroy();return}if(LT(e,t,n))return;if(!BT(e,JA,{enforce:YA})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
601
603
|
\r
|
|
602
|
-
`),t.destroy();return}let r=
|
|
604
|
+
`),t.destroy();return}let r=dg(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
603
605
|
\r
|
|
604
|
-
`),t.destroy();return}let o=r.sessionName;if(!
|
|
606
|
+
`),t.destroy();return}let o=r.sessionName;if(!_e(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
605
607
|
\r
|
|
606
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
608
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;QF.handleUpgrade(e,t,n,i=>cg(i,o,{cols:s,rows:a}))};nE.on("upgrade",rE);var oE=process.env.BOSTRAT_CERT_DIR||we.join(process.env.HOME||Yh.tmpdir(),".bostrat","certs"),qA=Number(process.env.BOSTRAT_TLS_PORT)||3443,KA=we.join(oE,"bostrat.globopi.key"),GA=we.join(oE,"bostrat.globopi.crt");if(Ye.existsSync(KA)&&Ye.existsSync(GA)){let e=HF.createServer({key:Ye.readFileSync(KA),cert:Ye.readFileSync(GA)},S);e.on("upgrade",rE),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(qA,Au,()=>{console.log(`[bostrat-agent] https listening on ${Au||"0.0.0.0"}:${qA}`)})}nE.listen(HA,Au,()=>{console.log(`[bostrat-agent] listening on ${Au||"0.0.0.0"}:${HA}`);let e=sv(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: ${zA}%`),console.log(`[bostrat-agent] ip allowlist: ${Ds.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Xh.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Mm().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Lm().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),$m().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)),vm().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)),aU(),nU(),sU(),rk({listTracks:ae,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)),SS()&&vS({listTracks:ae}),cA(),pA(),Jv({resolveTrack:ne}),sT({listTracks:ae,createTrack:Pl,archiveTrack:io,listRepos:cn,resolveTrack:ne,sayToLead:_t,answerPrompt:Cl,startLeadHandoff:Ll,interruptLead:Nl,listSubtaskJobs:co,cancelSubtask:Hl,readEvents:L,appendEvent:_,divertShip:jh,answerPrExists:Lh,knownAgents:ii}),eU(),ra().then(t=>{t||console.warn(`[boot] no host tmux server on ${ft} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),_A(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(_u=uv({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Yh.hostname(),name:process.env.BOSTRAT_NODE_NAME||Yh.hostname(),listTracks:ae,detectAgents:$l,sampleMetrics:cT,onTracksTransferred:Bm,onUpdateAvailable:async t=>{try{Tu={latest:t.latest,current:t.current},await on({...t,policy:ut()}),ut()==="auto"&&!gi(await Ss())&&Bs.arm({to:t.latest,from:t.current,kind:Ft})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>GF(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await on({current:t,latest:a,min:n,installKind:o,required:!0,policy:ut(),status:"available",reason:"refused"}),ut()!=="off"&&a){let i=await zl({to:a,kind:Ft,reason:"hard-floor"});i.ok?(Bs.cancel(),await on({current:t,latest:a,min:n,installKind:o,required:!0,policy:ut(),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:ut}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),Ao().catch(()=>{}),mS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Yv().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),j0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),tS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),qk().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{_u?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function eU(){tU(),wl().catch(()=>{}),setInterval(()=>wl().catch(()=>{}),1e4)}function tU(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=we.resolve(VA,"..","bin",e),n=we.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Ye.existsSync(t))continue;Ye.mkdirSync(n,{recursive:!0});let r=we.join(n,e);(Ye.existsSync(r)?Ye.readlinkSync(r):null)!==t&&(Ye.rmSync(r,{force:!0}),Ye.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 nU(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ae(),r=await ST({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()}var rU=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,oU=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function sU(){let e=async()=>{try{let n=await Pw();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await ae()).filter(a=>!a.archivedAt&&!a.transferredTo&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await Nw([...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,rU).unref()},oU).unref()}function aU(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ae();for(let n of t)n.archivedAt||n.transferredTo||!n.boxes?.some(r=>r.state==="live")||(await Ol(n.id).catch(()=>{}),await w0(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|