bostrat-node 1.8.4 → 1.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +51 -51
- package/dist/index.js +212 -197
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var Z$=Object.defineProperty;var H=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var tf=(e,t)=>{for(var n in t)Z$(e,n,{get:t[n],enumerable:!0})};import{createHash as Q$,randomBytes as eR,timingSafeEqual as tR}from"node:crypto";function uy(){return eR(32).toString("hex")}function Ea(e){let t=typeof e=="string"?e.trim():"";return t?Q$("sha256").update(t).digest("hex"):null}function dy(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:tR(Buffer.from(e),Buffer.from(t))}function _a(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(nf.set(e,(nf.get(e)||0)+1),rf.has(e)&&n-rf.get(e)<nR)return!1;rf.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=${nf.get(e)}${o?` ${o}`:""}`),!0}async function fy(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 Mn,Nn,nf,rf,nR,es=H(()=>{Mn="BOSTRAT_HOOK_TOKEN",Nn="x-bostrat-hook";nf=new Map,rf=new Map,nR=6e4});import _t from"node:fs/promises";import py from"node:os";import St from"node:path";import{execFile as rR}from"node:child_process";import{promisify as oR}from"node:util";function Uc(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function sR(){return process.env.CLAUDE_CONFIG_DIR?St.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):St.join(py.homedir(),".claude.json")}function aR(){return process.env.CLAUDE_CONFIG_DIR?St.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):St.join(py.homedir(),".claude","settings.json")}async function cr(e){if(!e||typeof e!="string")return;let t=St.resolve(e),n=sR(),r=aR(),o={};try{o=JSON.parse(await _t.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 _t.mkdir(St.dirname(n),{recursive:!0}),await _t.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await _t.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.remoteControlAtStartup=!1,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 "${Nn}: $${Mn}" --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 _t.mkdir(St.dirname(r),{recursive:!0}),await _t.writeFile(r,JSON.stringify(i,null,2)),await iR(t)}async function iR(e){try{let t=St.resolve(e),n=St.join(t,".bostrat","memory","claude"),r=St.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await _t.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(St.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await _t.mkdir(n,{recursive:!0}),await _t.mkdir(St.dirname(r),{recursive:!0}),await _t.writeFile(r,JSON.stringify(o,null,2)),await cR(t,["/.claude/settings.local.json"])}catch{}}async function cR(e,t){try{let{stdout:n}=await oR(rR)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=St.resolve(e,n.trim()),o=await _t.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await _t.mkdir(St.dirname(r),{recursive:!0}),await _t.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var Ia=H(()=>{es()});import of from"node:fs/promises";import lR from"node:os";import Hc from"node:path";function uR(){let e=process.env.CODEX_HOME||Hc.join(lR.homedir(),".codex");return Hc.join(e,"config.toml")}function my(e){return`[projects."${e}"]`}function dR(e,t){let n=my(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 ts(e){if(!e||typeof e!="string")return;let t=Hc.resolve(e),n=uR(),r="";try{r=await of.readFile(n,"utf8")}catch{}if(dR(r,t))return;let o=my(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
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await of.mkdir(Hc.dirname(n),{recursive:!0}),await of.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Wc=H(()=>{});import hy from"node:fs/promises";import fR from"node:os";import $a from"node:path";function pR(e){let n=$a.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||$a.join(fR.homedir(),".cursor");return $a.join(r,"projects",n)}async function ns(e){if(!e||typeof e!="string")return;let t=$a.resolve(e),n=pR(t),r=$a.join(n,".workspace-trusted");await hy.mkdir(n,{recursive:!0}),await hy.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var qc=H(()=>{});import af from"node:fs/promises";import lr from"node:path";import{execFile as mR}from"node:child_process";import{promisify as hR}from"node:util";async function xR(e){try{return(await af.stat(lr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function vR(e){let t=i=>af.readFile(lr.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 Pn(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await xR(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:lr.basename(o),branch:await vR(o)});return}if(s>=bR)return;let i;try{i=await af.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||kR.has(c.name)||await t(lr.join(o,c.name),s+1)}for(let o of yR)await t(lr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await Ee(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 SR(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Ee(e){if(!e)return null;if(sf.has(e))return sf.get(e);let t=null;try{let{stdout:n}=await wR("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=SR(n)}catch{t=null}return sf.set(e,t),t}async function rs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await Pn())if((await Ee(n.path))?.toLowerCase()===t)return n.path;return null}var wR,wy,gR,yR,bR,kR,sf,ur=H(()=>{wR=hR(mR),wy=process.env.HOME||"/home/globo",gR=lr.join(process.env.BOSTRAT_STATE_DIR||lr.join(wy,".bostrat"),"repos"),yR=[...(process.env.BOSTRAT_REPOS_ROOT||lr.join(wy,"Documents")).split(","),gR].map(e=>e.trim()).filter(Boolean),bR=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,kR=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);sf=new Map});import TR from"node:fs";import gy from"node:fs/promises";import Ra from"node:path";import AR from"node:os";import{fileURLToPath as ER}from"node:url";import{execFile as _R}from"node:child_process";import{promisify as IR}from"node:util";function Oe(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 Gc(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Kc(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!by.test(n))throw os("repo must be owner/name",400);let r=cf.get(n);if(r&&r.expiresAt-Date.now()>OR)return r;if(!Gc(t))throw os("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await ky(`${Oe(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:xy(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw os(`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 os(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 cf.set(n,a),a}async function Oa({env:e=process.env,force:t=!1}={}){if(!Gc(e))return{configured:!1,repos:[],username:null};if(!t&&no&&Date.now()-no.at<CR)return no.data;try{let n=await ky(`${Oe(e)}/v1/github/repos`,{headers:xy(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return no={at:Date.now(),data:r},r}catch(n){return no?no.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function vy(){cf.clear(),no=null}async function pn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Ee(e):null);if(!r)return{};let{token:o}=await Kc(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function lf({env:e=process.env}={}){return(await Oa({env:e})).username??null}function DR(e){let[t,n]=String(e).split("/");return Ra.join(RR,t,n)}async function uf(e,{env:t=process.env}={}){let n=(...r)=>yy("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${NR()}" ${PR(t)}`),await n("config","credential.useHttpPath","true")}async function Vc(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!by.test(String(e||"")))throw os(`bad repo slug "${e}"`,400);let r=DR(e);if(TR.existsSync(Ra.join(r,".git")))return r;let{token:o}=await Kc(e,{env:t});await gy.mkdir(Ra.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await yy("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:MR,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await gy.rm(r,{recursive:!0,force:!0}).catch(()=>{}),os(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await uf(r,{env:t}),r}var yy,$R,RR,by,OR,CR,MR,ky,cf,no,xy,os,NR,PR,ct=H(()=>{ur();yy=IR(_R),$R=process.env.BOSTRAT_STATE_DIR||Ra.join(process.env.HOME||AR.tmpdir(),".bostrat"),RR=Ra.join($R,"repos"),by=/^[^/\s]+\/[^/\s]+$/,OR=5*6e4,CR=6e4,MR=10*6e4,ky=fetch,cf=new Map,no=null;xy=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),os=(e,t)=>Object.assign(new Error(e),{status:t});NR=()=>ER(new URL("../bin/bostrat-git-credential",import.meta.url)),PR=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Ty(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ey(e,t,n,r=Sy){let o=Oe(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await jR(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function _y(e,{env:t=process.env,force:n=!1,timeoutMs:r=Sy}={}){if(!Ty(t))return{facts:[],source:"unconfigured"};let o=df.get(e);if(!n&&o&&Date.now()-o.at<BR)return{facts:o.facts,source:"cache"};try{let s=await Ey(`/v1/memory/${encodeURIComponent(e)}`,{headers:Ay(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 df.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 zc(e,t,{env:n=process.env}={}){if(!Ty(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Ey(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Ay(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 df.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 BR,Sy,df,jR,Ay,Iy=H(()=>{ct();BR=6e4,Sy=5e3,df=new Map,jR=(...e)=>fetch(...e);Ay=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import ff from"node:fs/promises";import ss from"node:path";import LR from"node:os";import{EventEmitter as FR}from"node:events";import{randomUUID as UR}from"node:crypto";function as(e){return e?.cwd?ss.join(e.cwd,".bostrat","events"):ss.join(pf,"events")}async function GR(e,t){if(!Yc.has(t)){let o=(await W(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Yc.set(t,o)}let n=Yc.get(t)+1;return Yc.set(t,n),n}function mf(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||!HR.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!WR.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!qR.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?ss.join(pf,"events"):as(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let u=e.boxes.find(f=>f.name===t.boxId);o=mf(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await GR(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||UR(),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}:{}},c=JSON.stringify(i),l=i;return Ry(c)&&(c=Oy(c),l=JSON.parse(c)),await ff.mkdir(r,{recursive:!0}),await ff.appendFile($y(r,n),c+`
|
|
15
|
+
`),lt.emit("event",l),lt.emit(`track:${n}`,l),l}async function W(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?ss.join(pf,"events"):as(e));let o;try{o=await ff.readFile($y(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(Ry(a)?Oy(a):a))}catch{}return s}function Cy(e,t){let n=`track:${e}`;return lt.on(n,t),()=>lt.off(n,t)}var HR,WR,qR,lt,pf,$y,Yc,KR,Ry,Oy,ge=H(()=>{HR=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","code.committed","code.rework","rework.cause.offered","rework.cause.answered","rework.cause.retracted","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","loop.create.offered","loop.create.answered","loop.created","loop.update.offered","loop.update.answered","loop.updated","loop.delete.offered","loop.delete.answered","loop.deleted","loop.paused","followup.armed","followup.armed.cancelled","followup.offered","followup.answered","followup.create.offered","followup.create.answered","followup.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"]),WR=new Set(["user","agent","orchestrator","system","peer"]),qR=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),lt=new FR;lt.setMaxListeners(0);pf=process.env.BOSTRAT_STATE_DIR||ss.join(process.env.HOME||LR.tmpdir(),".bostrat");$y=(e,t)=>ss.join(e,`${t}.ndjson`),Yc=new Map;KR=/(?<!\\)((?:\\\\)*)\\u0000/g,Ry=e=>e.includes("\\u0000"),Oy=e=>e.replace(KR,"$1\\ufffd")});import ee from"node:fs/promises";import VR from"node:os";import be from"node:path";import{execFile as zR}from"node:child_process";import{promisify as YR}from"node:util";function Xc(){return be.join(Zc,"memory","repos")}function ro(e){return e===Dn?be.join(Zc,"memory",Dn):be.join(Xc(),e)}async function Ma(e){try{return JSON.parse(await ee.readFile(be.join(e,Ly),"utf8"))}catch{return{}}}async function wf(e,t){let n=await Ma(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await ee.mkdir(e,{recursive:!0}),await ee.writeFile(be.join(e,Ly),JSON.stringify(n))}function Ny(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 Py(e){try{let{stdout:t}=await JR("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function gf(e){if(!e)return[];let t=Ny(await Py(e));if(t)return[t];let n=[],r=[];try{r=await ee.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Ny(await Py(be.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Qc(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 dr(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function mn(e,t=null){let n=t??await ee.readFile(e,"utf8").catch(()=>""),{fm:r}=Qc(n),o=dr(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await ee.stat(e)).mtimeMs}catch{return 0}}function Fy(e,t){let n=dr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Dn){let o=dr(e,"trust");if(!o||/^operator/i.test(o))return Dn}return t}function yf(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
|
-
`)){let l=
|
|
18
|
+
`)){let l=cs(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=cs(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
21
|
+
`)}function Uy(e,t){let n=dr(t,"name")||e.replace(/\.md$/,""),r=dr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Na(e){let t=[];try{t=await ee.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 Hy(e,t){let n=be.join(e,".bostrat","memory"),r=await Na(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Dy.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let f of r){let d=be.join(n,f);if(o)try{if((await ee.stat(d)).mtimeMs<o-ZR)continue}catch{continue}let p;try{p=await ee.readFile(d,"utf8")}catch{continue}let{fm:h}=Qc(p),w=Fy(h,t);if(!w)continue;let g=ro(w),b=be.join(g,f),v=await mn(d,p);if(l.has(g)||l.set(g,await Ma(g)),(l.get(g)[f]||0)>=v)continue;let x=await mn(b).catch(()=>0);if(await ee.access(b).then(()=>!0).catch(()=>!1)&&x>=v)continue;await ee.mkdir(g,{recursive:!0}),await ee.writeFile(b,p),c++;let P=a.get(g);P||a.set(g,P={won:new Set,generated:new Map}),P.won.add(f),P.generated.set(f,Uy(f,h)),i.has(w)||i.set(w,new Set),i.get(w).add(f)}let u=await ee.readFile(be.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[f,d]of a){let p=be.join(f,"MEMORY.md"),h=await ee.readFile(p,"utf8").catch(()=>"");await ee.writeFile(p,yf(h,u,d.won,d.generated))}return Dy.set(e,s),{harvested:c,changedByKey:i}}async function Wy(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await ee.access(be.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await gf(r),a=await Hy(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 QR(){let e=be.join(Zc,"workspaces"),t=[];try{t=await ee.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return Wy(t.filter(n=>n.isDirectory()).map(n=>be.join(e,n.name)))}async function nO(e,t){let n=ro(e),r=await Ma(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!eO.test(l)||l==="MEMORY.md")continue;let u=be.join(n,l),f=await ee.access(u).then(()=>!0).catch(()=>!1),d=f?await mn(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await ee.rm(u,{force:!0}),await wf(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await ee.mkdir(n,{recursive:!0}),await ee.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=be.join(n,"MEMORY.md"),l=await ee.readFile(c,"utf8").catch(()=>""),u=yf(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
23
|
-
`).filter(d=>!f.has(
|
|
24
|
-
`)}await
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
23
|
+
`).filter(d=>!f.has(cs(d))).join(`
|
|
24
|
+
`)}await ee.mkdir(n,{recursive:!0}),await ee.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function el(e,t=null){let n=ro(e),r=t?.length?t:await Na(n);if(!r.length)return{pushed:0,source:"empty"};let o=tO(await ee.readFile(be.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=be.join(n,a),c;try{c=await ee.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await mn(i,c),indexLine:o.get(a)??null})}return zc(e,s)}async function rO(e,{timeoutMs:t}={}){let{facts:n,source:r}=await _y(e,t?{timeoutMs:t}:{});n.length?await nO(e,n):r==="plane"&&await el(e)}function oO(e){let t=e.split(`
|
|
25
|
+
`);t.length>My&&(t=t.slice(0,My));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>XR&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function aO(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=be.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let f=ro(u),d=await Ma(f);for(let[g,b]of Object.entries(d)){let v=be.join(n,g);await ee.access(v).then(()=>!0).catch(()=>!1)&&await mn(v)<=b&&(await ee.rm(v,{force:!0}),o++)}let p=await Na(f);if(!p.length)continue;await ee.mkdir(n,{recursive:!0});for(let g of p){let b=be.join(f,g),v=be.join(n,g),x=await mn(b),I=await mn(v).catch(()=>0);await ee.access(v).then(()=>!0).catch(()=>!1)&&I>=x||(await ee.copyFile(b,v),r++)}let h=await ee.readFile(be.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===Dn?"## operator memory":`## repo: ${u}`;h.trim()&&s.push(t.length>1?`${w}
|
|
28
28
|
|
|
29
29
|
${h.trim()}`:h.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
|
|
30
30
|
|
|
31
31
|
`)+`
|
|
32
|
-
`,c=
|
|
33
|
-
`).filter(p=>!s.has(
|
|
34
|
-
`);await
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=oO(i);await ee.mkdir(n,{recursive:!0}),await ee.writeFile(be.join(n,"MEMORY.md"),c);let l=be.join(n,"claude");return await ee.mkdir(l,{recursive:!0}),await ee.writeFile(be.join(l,"MEMORY.md"),sO,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function qy(e){try{let t=await gf(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,Dn];return await Promise.all(n.map(r=>rO(r,{timeoutMs:iO}))),await aO(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Gy(){return{...Qe,pending:is.size,queued:hf.size}}function Ky(e){if(Jc.has(e))return Jc.get(e);hf.add(e);let t=By.then(async()=>{hf.delete(e),Jc.delete(e);let n=await kf(e);return Qe.runs++,Qe.lastRunAt=Date.now(),n.error?(Qe.errors++,Qe.lastError=n.error):Qe.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Jc.set(e,t),By=t.then(()=>{}),t}function tl(e,{delayMs:t=cO}={}){if(!e||typeof e!="string")return;Qe.scheduled++;let n=is.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{is.delete(e),Ky(e)},t);r.unref?.(),is.set(e,r)}async function bf(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await ee.access(be.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=is.get(e);r&&(clearTimeout(r),is.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([Ky(e),s])}finally{clearTimeout(o)}}async function Vy(e){return Ca||(Ca=(async()=>{try{jy||(jy=!0,await uO());let{harvested:t,changedByKey:n}=await Wy(e);for(let[r,o]of n)await el(r,[...o]);return Qe.reconciles++,Qe.lastReconcileAt=Date.now(),Qe.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return Qe.errors++,Qe.lastError=t?.message||String(t),{harvested:0,error:Qe.lastError}}finally{Ca=null}})(),Ca)}function lO(){return be.join(Zc,"memory",".full-sweep-done")}async function zy(){let e=lO();try{let t=JSON.parse(await ee.readFile(e,"utf8"));return Qe.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...Qe.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await QR();for(let[o,s]of n)await el(o,[...s]);let r={at:Date.now(),harvested:t};return await ee.mkdir(be.dirname(e),{recursive:!0}),await ee.writeFile(e,JSON.stringify(r)),Qe.backfill=r,{ran:!0,...r}}catch(t){return Qe.errors++,Qe.lastError=t?.message||String(t),{ran:!1,error:Qe.lastError}}}async function uO(){let e=0,t=[];try{t=await ee.readdir(Xc(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=be.join(Xc(),n.name),o=ro(Dn),s=new Set,a=new Map;for(let p of await Na(r)){let h=be.join(r,p),w;try{w=await ee.readFile(h,"utf8")}catch{continue}let{fm:g}=Qc(w);if(Fy(g,n.name)!==Dn)continue;let b=be.join(o,p),v=await mn(b).catch(()=>0),x=await mn(h,w);await ee.mkdir(o,{recursive:!0}),x>v&&await ee.writeFile(b,w),await ee.rm(h,{force:!0}),s.add(p),a.set(p,Uy(p,g)),e++}if(!s.size)continue;let i=Date.now();await wf(r,[...s].map(p=>[p,i])),await zc(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=be.join(r,"MEMORY.md"),l=await ee.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(p=>!s.has(cs(p))).join(`
|
|
34
|
+
`);await ee.writeFile(c,u).catch(()=>{});let f=be.join(o,"MEMORY.md"),d=await ee.readFile(f,"utf8").catch(()=>"");await ee.writeFile(f,yf(d,l,s,a))}return{moved:e}}async function Yy(){let e=[],t=[Dn];try{for(let n of await ee.readdir(Xc(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=ro(n),o=await Na(r),s=await Ma(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await ee.readFile(be.join(r,i),"utf8").catch(()=>""),{fm:l}=Qc(c);a.push({file:i,name:dr(l,"name")||i.replace(/\.md$/,""),description:dr(l,"description")||"",scope:dr(l,"scope")||null,modified:await mn(be.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function Jy(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=ro(e),r=be.join(n,t);if(!await ee.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await ee.rm(r,{force:!0}),await wf(n,[[t,s]]);let a=be.join(n,"MEMORY.md"),i=await ee.readFile(a,"utf8").catch(()=>"");await ee.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>cs(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await zc(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function kf(e){try{let t=await gf(e),{harvested:n,changedByKey:r}=await Hy(e,t[0]||null);for(let[o,s]of r)await el(o,[...s]);return n>0&&await fO(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function fO(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,dO)}}await $({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var JR,Zc,My,XR,Dn,Ly,cs,Dy,ZR,eO,tO,sO,iO,cO,is,hf,By,Qe,Jc,jy,Ca,dO,Pa=H(()=>{Iy();ge();JR=YR(zR),Zc=process.env.BOSTRAT_STATE_DIR||be.join(process.env.HOME||VR.tmpdir(),".bostrat"),My=200,XR=25*1024;Dn="operator";Ly=".tombstones.json";cs=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Dy=new Map,ZR=2e3;eO=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,tO=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=cs(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};sO=`# Track memory
|
|
38
38
|
|
|
39
39
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
40
40
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
41
41
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
42
|
-
`;
|
|
43
|
-
`,n!=null?{mode:n}:void 0),await
|
|
44
|
-
`,"utf8");let n=(await
|
|
45
|
-
`).filter(Boolean);n.length>
|
|
42
|
+
`;iO=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;cO=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,is=new Map,hf=new Set,By=Promise.resolve(),Qe={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Jc=new Map;jy=!1,Ca=null;dO=20});import xf from"node:fs/promises";import pO from"node:path";async function Tt(e,t,{mode:n}={}){await xf.mkdir(pO.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++mO}.tmp`;await xf.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await xf.rename(r,e)}var mO,fr=H(()=>{mO=0});import tb from"node:fs/promises";import nb from"node:os";import rb from"node:path";function je(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ja(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),hO);try{return await xO(`${Oe(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function sb(){return rl||(vf||=tb.readFile(ob(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>rl={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>vf=null),vf)}async function ol(){await Tt(ob(),rl).catch(()=>{})}function Qy(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Tf(e){let t=await sb(),n=Qy(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=>Qy(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>Xy&&(t.ops=t.ops.slice(-Xy)),await ol()}function eb(){nl||(nl=setTimeout(()=>{nl=null,ls().catch(()=>{})},vO),nl.unref?.())}function Sf(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function TO(e,t=process.env){if(e.op==="record"){let n=await ja(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Ba(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:kO()}]})},t);if(!n.ok)throw Sf(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 tb.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await ja(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Ba(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Sf(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 ja(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Ba(t)},t);if(!n.ok)throw Sf(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function ls({env:e=process.env,log:t=console,now:n=Date.now()}={}){return je(e)?Da||(Da=(async()=>{let r=await sb();AO(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await TO(a,e)}catch(c){if(!c?.refused)return eb(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<gO)return eb(),await ol(),{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>Zy&&(r.parked=r.parked.slice(-Zy)),s++,await ol();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await ol()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Da=null}),Da):{flushed:0,source:"unconfigured"}}function AO(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=SO);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 gt(e,t,{trackId:n=null,env:r=process.env}={}){return je(r)?(await Tf({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),ls({env:r})):{queued:!1,source:"unconfigured"}}async function pr(e,t,n,r,o,{env:s=process.env}={}){return je(s)?(await Tf({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),ls({env:s})):{queued:!1,source:"unconfigured"}}async function Xt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return je(r)?(await Tf({op:"delete",family:e,id:t,deletedAt:n}),ls({env:r})):{queued:!1,source:"unconfigured"}}async function rt(e,{env:t=process.env}={}){if(!je(t))return{records:[],source:"unconfigured"};try{let n=await ja(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Ba(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 La(e,t,n,r,{env:o=process.env}={}){if(!je(o))return null;try{let s=await ja(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ba(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 hO,wO,Xy,gO,Zy,yO,bO,ob,kO,xO,vO,SO,Ba,rl,vf,nl,Da,mr=H(()=>{ct();fr();hO=1e4,wO=3e4,Xy=2e3,gO=5,Zy=100,yO=60*6e4,bO=()=>process.env.BOSTRAT_STATE_DIR||rb.join(process.env.HOME||nb.tmpdir(),".bostrat"),ob=()=>rb.join(bO(),"content-outbox.json"),kO=()=>process.env.BOSTRAT_NODE_ID||nb.hostname(),xO=(...e)=>fetch(...e),vO=wO,SO=yO;Ba=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});rl=null,vf=null,nl=null,Da=null});import Af from"node:fs/promises";import ab from"node:path";async function ib(e,t,{box:n=null,agent:r=null}={}){if(!e||!je())return{pushed:!1};try{let o=Date.now();return await gt("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await pr("checkpoints",e,o,"md",t),Fa=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function RO(){if(Fa&&Date.now()-Fa.at<$O)return{records:Fa.records,source:"plane"};let e=await rt("checkpoints");return e.source==="plane"&&(Fa={at:Date.now(),records:e.records}),e}async function sl(e){try{let t=IO(e);if(!t||!je())return{restored:!1};let n=ab.join(e,EO,_O);if(await Af.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await RO();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 La("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Af.mkdir(ab.dirname(n),{recursive:!0}),await Af.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var EO,_O,IO,$O,Fa,al=H(()=>{mr();EO=".bostrat/handoff",_O="latest.md",IO=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;$O=6e4,Fa=null});import Ua from"node:fs/promises";import OO from"node:os";import lb from"node:path";function oo(e){let t=String(e??"");for(let n of CO)t=t.replace(n,"\u2039redacted\u203A");return t}function il(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function MO(e){try{await Ua.mkdir(Ef,{recursive:!0}),await Ua.appendFile(us,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await Ua.readFile(us,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>cb&&await Ua.writeFile(us,`${n.slice(-cb).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=
|
|
49
|
-
`,r=
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${us}: ${t?.message||t}`)}}async function ue(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&&il())try{t.paneTail=oo((await De(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(il()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch|submit-|gate-accept|blocked|timeout|dialog)/.test(String(t.phase||t.step||"")))&&await MO(t)}async function ub({limit:e=80}={}){try{return(await Ua.readFile(us,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=oo(o.paneTail)),o}catch{return{raw:oo(r)}}})}catch{return[]}}var CO,Ef,us,cb,db,hn=H(()=>{ut();CO=[/[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];Ef=process.env.BOSTRAT_DEBUG_DIR||lb.join(OO.homedir(),".bostrat","debug"),us=lb.join(Ef,"handoff.jsonl"),cb=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;db={DEBUG_DIR:Ef,LOG_FILE:us}});import cl from"node:fs/promises";import _f from"node:path";import NO from"node:os";async function pb(e=fb){await cl.mkdir(e,{recursive:!0});for(let t of DO){let n=t==="prepare-commit-msg"?BO:t==="post-commit"?jO:`${If}exit 0
|
|
49
|
+
`,r=_f.join(e,t),o=null;try{o=await cl.readFile(r,"utf8")}catch{}o!==n&&await cl.writeFile(r,n,{mode:493}),await cl.chmod(r,493).catch(()=>{})}return e}function mb({nodeUrl:e,dir:t=fb,env:n=process.env}={}){return String(n.BOSTRAT_GIT_HOOKS||"").toLowerCase()==="off"?[]:["-e","GIT_CONFIG_COUNT=1","-e","GIT_CONFIG_KEY_0=core.hooksPath","-e",`GIT_CONFIG_VALUE_0=${t}`,"-e",`${Ha}=${e}`]}function hb(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Ha,PO,fb,DO,If,BO,jO,$f=H(()=>{es();Ha="BOSTRAT_NODE_URL",PO=process.env.BOSTRAT_STATE_DIR||_f.join(process.env.HOME||NO.tmpdir(),".bostrat"),fb=_f.join(PO,"git-hooks"),DO=["applypatch-msg","pre-applypatch","post-applypatch","pre-commit","pre-merge-commit","prepare-commit-msg","commit-msg","post-commit","pre-rebase","post-checkout","post-merge","pre-push","post-rewrite","pre-auto-gc"],If=`#!/bin/sh
|
|
50
50
|
# bostrat git hook shim (agent/src/git-hooks.js) \u2014 chains to the repo's own hook,
|
|
51
51
|
# then stamps/reports for rework attribution when running inside a bostrat box.
|
|
52
52
|
hook=$(basename "$0")
|
|
@@ -61,73 +61,73 @@ esac
|
|
|
61
61
|
if [ -x "$orig/$hook" ] && [ "$orig/$hook" != "$0" ]; then
|
|
62
62
|
"$orig/$hook" "$@" || exit $?
|
|
63
63
|
fi
|
|
64
|
-
`,
|
|
64
|
+
`,BO=`${If}
|
|
65
65
|
# \u2500\u2500 bostrat: append provenance trailers (Layer 0). Skipped for merge commits
|
|
66
66
|
# (nobody "wrote" a merge) and outside a box (no token / no node url).
|
|
67
|
-
[ -z "$${
|
|
68
|
-
[ -z "$${
|
|
67
|
+
[ -z "$${Mn}" ] && exit 0
|
|
68
|
+
[ -z "$${Ha}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${Nn}: $${Mn}" "$${Ha}/api/git/trailers" 2>/dev/null) || exit 0
|
|
71
71
|
[ -z "$trailers" ] && exit 0
|
|
72
72
|
printf '%s\\n' "$trailers" | while IFS= read -r line; do
|
|
73
73
|
[ -n "$line" ] && git interpret-trailers --in-place --if-exists addIfDifferent --trailer "$line" "$1" >/dev/null 2>&1 || true
|
|
74
74
|
done
|
|
75
75
|
exit 0
|
|
76
|
-
`,
|
|
76
|
+
`,jO=`${If}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
|
-
[ -z "$${
|
|
79
|
-
[ -z "$${
|
|
78
|
+
[ -z "$${Mn}" ] && exit 0
|
|
79
|
+
[ -z "$${Ha}" ] && exit 0
|
|
80
80
|
sha=$(git rev-parse HEAD 2>/dev/null) || exit 0
|
|
81
81
|
cwd=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
82
|
-
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${
|
|
83
|
-
-H 'content-type: application/json' -H "${
|
|
82
|
+
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${Ha}/api/git/committed" \\
|
|
83
|
+
-H 'content-type: application/json' -H "${Nn}: $${Mn}" --data-binary @- >/dev/null 2>&1 || true
|
|
84
84
|
exit 0
|
|
85
|
-
`});import{execFile as
|
|
86
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,p,h]=r.split("|"),w=Number(i),g=Number(h);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!
|
|
87
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
88
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
89
|
-
`);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
|
|
90
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function
|
|
91
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
92
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function
|
|
93
|
-
`)){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(
|
|
94
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function
|
|
95
|
-
`)){let p=d.trim();if(!p||p[0]!=="{")continue;let h;try{h=JSON.parse(p)}catch{continue}if((h.type==="turn_context"&&h.payload?.model||h.type==="session_meta"&&h.payload?.model)&&(i=h.payload.model),h.type==="event_msg"){let
|
|
96
|
-
`;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
|
|
97
|
-
`)){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"&&
|
|
98
|
-
`).trim():"";
|
|
99
|
-
`).trim():""}function
|
|
100
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let w of l.content){let g=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof g=="string"&&
|
|
101
|
-
|
|
102
|
-
`):""}var
|
|
103
|
-
`);if(!
|
|
104
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
105
|
-
`);return t}function
|
|
106
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
107
|
-
`).pop()||"";return
|
|
108
|
-
`),o=r.findIndex(i=>
|
|
109
|
-
`).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
|
|
110
|
-
`),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
|
|
111
|
-
`),n=-1;for(let s=t.length-1;s>=0;s--)if(t[s].trim()){n=s;break}if(n<0)return!1;let r=t[n];if(
|
|
112
|
-
`),r=-1;for(let o=n.length-1;o>=0;o--)if(
|
|
113
|
-
`)}function
|
|
114
|
-
`))
|
|
115
|
-
`).slice(-
|
|
116
|
-
`);return
|
|
85
|
+
`});import{execFile as gb}from"node:child_process";import{promisify as LO}from"node:util";import ul from"node:fs";import{randomUUID as yb}from"node:crypto";function ps(e){return new Promise(t=>setTimeout(t,e))}function ll(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 bb(e){return`/tmp/tmux-${e}/default`}function FO({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=bb(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function qa(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Wa}=1`]}function Ga(e,t,n){let r=so(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&&Le(n)&&(s+=` --name ${n}`),s}function so(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function hr(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function WO(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:so(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function kb(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await xb(e,{loginShell:t});return r===dl?!0:r===ds?!1:n}async function xb(e,{loginShell:t=!1,timeoutMs:n=Rf}={}){if(!e)return ds;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Ce(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),dl}catch(s){return s?.killed||s?.signal?fs:s?.code===1||s?.code===127?ds:fs}}async function fl(e,{retries:t=qO,sleep:n=GO,probe:r=xb}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Rf});for(let a=0;s===fs&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Rf*2});return s}function Z(...e){return["-S",It,...e]}async function wr(){if(!ul.existsSync(It))return!1;try{return await Ce(J,Z("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ka(){if(await wr())return!0;try{ul.mkdirSync(It.replace(/\/[^/]+$/,""),{recursive:!0}),await Ce(J,Z("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await wr()}function Le(e){return typeof e=="string"&&KO.test(e)}async function Zt(){if(!ul.existsSync(It))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 Ce(J,Z("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,p,h]=r.split("|"),w=Number(i),g=Number(h);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!HO.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ke(e){return(await Zt()).find(n=>n.name===e)||null}function YO(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=zO&&VO.test(t)}function pl(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function vb(e){try{let{stdout:t}=await Ce(J,Z("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function ZO(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 QO(e,t){let n=Date.now()+Va(),r=ZO(t),o=!1,s=0;for(;;){if(await ps(JO),s+=1,!await vb(e))return;let a=await De(e,{lines:15}),i=!o&&XO.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Bn(e))return;if(i){o=!0,await ue({flow:"spawn",phase:"launch-retry",sessionName:e,agent:hr(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Ce(J,Z("send-keys","-t",e,"C-u")),await Ce(J,Z("send-keys","-t",e,"-l",t)),await Ce(J,Z("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await ue({flow:"spawn",phase:"launch-failed",sessionName:e,agent:hr(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function gr(e,t=UO,n={}){if(!YO(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=pl(r);if(!Le(o))throw new Error("resulting tmux session name is invalid");if(!await Ka())throw new Error(`host tmux server not running on ${It} and auto-start failed \u2014 start one: ${J} -S ${It} new-session -d -s keeper "sleep 2147483647"`);if(await vb(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Of,a=n.agent||hr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||yb(),l=i?s:Ga(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&so(s),d=n.firstRun===!0;d||(f&&await cr(t),a==="codex"&&await ts(t),a==="cursor"&&await ns(t),await qy(t),n.freshWorkspace!==!0&&await sl(t));let p=f&&!d?Uc():null,h=uy(),w=["-e",`${Mn}=${h}`];w.push(...mb({nodeUrl:hb()})),p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...qa());try{await Ce(J,Z("new-session","-d","-s",o,"-c",t,...w))}catch(g){throw ll(p?ll(g,p):g,h)}return await ao(o),await Ce(J,Z("set-option","-t",o,"@bostrat-session","1")),await Ce(J,Z("set-option","-t",o,"@bostrat-title",r)),await Ce(J,Z("set-option","-t",o,"@bostrat-sid",c)),await Ce(J,Z("set-option","-t",o,"@bostrat-agent",a)),await Ce(J,Z("send-keys","-t",o,l,"Enter")),WO(l)&&QO(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:h}}async function Qt(e){if(!Le(e))throw new Error("invalid session name");await Ce(J,Z("kill-session","-t",e))}async function Sb(e,t){if(Le(e))try{let n=t?Z("set-option","-t",e,"@bostrat-sid",t):Z("set-option","-u","-t",e,"@bostrat-sid");await Ce(J,n)}catch{}}async function De(e,{lines:t=40}={}){if(!Le(e))return"";try{let n=Z("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Ce(J,n);return String(r||"")}catch{return""}}async function Tb(e){if(!Le(e))return 0;try{let{stdout:t}=await Ce(J,Z("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
88
|
+
`).filter(Boolean).length}catch{return 0}}async function ao(e,{cols:t=nC,rows:n=rC}={}){if(!Le(e))return!0;if(await Tb(e))return!1;try{let r=await Ab(e);if(r.cols===t&&r.rows===n)return!0;await Ce(J,Z("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Ce(J,Z("set-window-option","-t",e,"window-size","latest")),await ps(80)}catch{}return!0}async function oC(e){if(!Le(e))return 0;try{let{stdout:t}=await Ce(J,Z("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Bn(e){let t=await oC(e);if(!t)return!1;try{return await Ce("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function Ab(e){if(!Le(e))return{cols:0,rows:0};try{let{stdout:t}=await Ce(J,Z("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 ml(e,{cols:t=eC,rows:n=tC}={}){if(Le(e)&&!await Tb(e))try{let r=await Ab(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Ce(J,Z("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Ce(J,Z("set-window-option","-t",e,"window-size","latest")),await ps(80))}catch{}}async function jn(e){return await ml(e),De(e,{lines:et})}async function He(e,t,{settleMs:n=40,kind:r}={}){if(!Le(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>sC.has(s));for(let s=0;s<o.length;s++)await Ce(J,Z("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await ps(n);return ue({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function ms(e,t,{kind:n}={}){if(!Le(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await Ce(J,Z("send-keys","-t",e,"-N",String(r),"BSpace")),ue({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function wl(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Le(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await Ce(J,Z("send-keys","-t",e,"-l",s)),n>0&&await ps(n);ue({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function aC(e,t){return new Promise((n,r)=>{let o=gb(J,Z(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function iC(e,t){let n=`bostrat-${yb().slice(0,8)}`;await aC(["load-buffer","-b",n,"-"],t),await Ce(J,Z("paste-buffer","-p","-d","-b",n,"-t",e))}async function tt(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!Le(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await iC(e,t):await Ce(J,Z("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await ps(a),await Ce(J,Z("send-keys","-t",e,"Enter"))}ue({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r,...s?{paste:!0}:{}}).catch(()=>{})}var Ce,J,wb,It,UO,Of,Wa,Cf,HO,dl,ds,fs,Rf,qO,GO,Mf,KO,VO,zO,Va,JO,XO,eC,tC,et,nC,rC,sC,hl,ut=H(()=>{Ia();Wc();qc();Pa();al();hn();es();$f();Ce=LO(gb);J=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";wb=typeof process.getuid=="function"?process.getuid():0,It=FO({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return ul.existsSync(bb(wb))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:wb}),UO=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Of=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Wa="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";Cf="bostrat-usage-probe",HO=new Set([Cf]);dl="present",ds="absent",fs="unknown",Rf=5e3,qO=[250,1e3,3e3],GO=e=>new Promise(t=>setTimeout(t,e));Mf={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"};KO=/^[A-Za-z0-9_.-]{1,64}$/;VO=/^[A-Za-z0-9 ._↻-]+$/,zO=64;Va=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,JO=1e3,XO=/not found|Unknown command/i;eC=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,tC=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,et=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,nC=Number(process.env.BOSTRAT_BOX_COLS)||120,rC=Number(process.env.BOSTRAT_BOX_ROWS)||40;sC=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);hl=1600});import za from"node:fs";import pC from"node:fs/promises";import hs from"node:path";import mC from"node:os";function Df(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function Bf(e){let t=hs.join(Pf,Df(e)),n;try{n=za.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=hs.join(t,o);try{let{mtimeMs:a}=za.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function en(e,t){if(!t)return null;let n=hs.join(Pf,Df(e),`${t}.jsonl`);return za.existsSync(n)?n:null}function Cb(e){if(!e)return[];let t=hs.join(Pf,Df(e)),n;try{n=za.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=za.statSync(hs.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function At(e,t=131072){let n=await pC.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 wC(e){let t=e.split(`
|
|
89
|
+
`);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 Ya(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=en(e,t),o=!0,!r)return null}else if(n){if(r=Bf(e),o=!1,!r)return null}else return null;let s;try{s=wC(await At(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Ob,s.model),i=Ob[s.model]||hC;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 Ob,hC,Pf,tn=H(()=>{Ob={"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},hC=2e5,Pf=process.env.BOSTRAT_CLAUDE_PROJECTS||hs.join(mC.homedir(),".claude","projects")});function gC(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===`
|
|
90
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function Ja(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 Xa(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 Pe(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(Pb(o)){t.has(o)&&r++;continue}n.push(o)}return n}function Db(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 kC(e){let t=Db(e),n=dt(Pe(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${We(n)}`:`Read lines ${t.from}\u2013${t.to} of ${We(n)}`:e.includes("-i")&&n?`Edit ${We(n)}`:null}function jf(e){if(e.includes("--files")){let s=dt(Pe(e,Mb)[0]);return s&&s!=="."?`List files in ${ws(s)}`:"List files"}let t=Pe(e,Mb),n=t[0];if(!n)return null;let r=dt(t.slice(1).find(s=>s!==".")),o=gs(n,40);return r?`Search for \u201C${o}\u201D in ${ws(r)}`:`Search for \u201C${o}\u201D`}function Lf(e){let t=Pe(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 Ff(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=Pe(e)[0];return n?`Run ${We(n)}`:null}function xC(e){let t=Pe(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 Nb(e){return e.find(t=>/^https?:\/\//.test(t))||null}function TC(e){let t=Xa(Ja(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(We(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return Uf(t[2]);let n=We(t[0]);if(SC.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=vC[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function Uf(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
91
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=gC(t);for(let i=0;i<r.length-1;i++){let c=Xa(Ja(r[i])),l=Xa(Ja(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(We(c[0]))&&We(l[0])==="sed"){let u=Db(l),f=Pe(c)[0];if(u&&f)return gs(u.from===u.to?`Read line ${u.from} of ${We(f)}`:`Read lines ${u.from}\u2013${u.to} of ${We(f)}`,88)}}let o=[];for(let i of r){let c=TC(i);c&&o.push({d:c,head:We(Xa(Ja(i))[0]||""),sub:Pe(Xa(Ja(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":gs(s?s.d:o[0].d,88)}var gs,dt,We,ws,Pb,Mb,yC,bC,vC,SC,Bb=H(()=>{gs=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,dt=e=>e&&!/[$`]/.test(e)?e:null,We=e=>e.replace(/\/+$/,"").split("/").pop()||e,ws=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},Pb=e=>e.startsWith("-"),Mb=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);yC={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 ${We(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"},bC={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"};vC={git:e=>{let t=Pe(e,new Set(["-C","-c"])),n=yC[t[0]];return n?n(t,e):null},gh:e=>{let t=Pe(e),n=bC[t[0]];return n?n(t):null},sed:kC,rg:jf,grep:jf,egrep:jf,nl:e=>{let t=dt(Pe(e)[0]);return t?`Read ${We(t)}`:null},cat:e=>{let t=dt(Pe(e)[0]);return t?`Read ${We(t)}`:null},head:e=>{let t=dt(Pe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${We(t)}`:null},tail:e=>{let t=dt(Pe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${We(t)}`:null},wc:e=>{let t=dt(Pe(e)[0]);return t?`Count lines in ${We(t)}`:"Count input lines"},ls:e=>{let t=dt(Pe(e)[0]);return t&&t!=="."?`List files in ${ws(t)}`:"List files"},find:e=>dt(e[1])&&!Pb(e[1])&&e[1]!=="."?`Find files in ${ws(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=dt(Pe(e)[0]);return t?`Create directory ${ws(t)}`:null},touch:e=>{let t=dt(Pe(e)[0]);return t?`Create ${We(t)}`:null},rm:e=>{let t=dt(Pe(e)[0]);return t?`Delete ${ws(t)}`:null},mv:e=>{let t=dt(Pe(e)[0]);return t?`Move ${We(t)}`:null},cp:e=>{let t=dt(Pe(e)[0]);return t?`Copy ${We(t)}`:null},chmod:e=>{let t=dt(Pe(e)[1]);return t?`Change permissions on ${We(t)}`:null},diff:e=>{let t=Pe(e);return t.length>=2?`Compare ${We(t[0])} and ${We(t[1])}`:null},curl:e=>{let t=Nb(e);return t?`Fetch ${gs(t,56)}`:"Fetch a URL"},wget:e=>{let t=Nb(e);return t?`Fetch ${gs(t,56)}`:"Fetch a URL"},npm:Lf,pnpm:Lf,yarn:Lf,npx:e=>{let t=Pe(e)[0];return t?`Run ${t}`:null},node:e=>Ff(e,"Node"),python:e=>Ff(e,"Python"),python3:e=>Ff(e,"Python"),make:e=>{let t=Pe(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:xC,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=Pe(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=Pe(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Pe(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},SC=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function ys(e){let t=e.split(`
|
|
92
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function yr(e,t=""){let n=t.trim();return n||Uf(e)||ys(e)}function Hf(e,t){if(e){if(Array.isArray(e)){for(let n of e)Hf(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Wf.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))Hf(n,t)}}}async function jb(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await At(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
93
|
+
`)){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(Hf(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:yr(f.command,f.description),ts:u})}return o.slice(-n)}function Lb(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((h,w)=>w>=s&&(h.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(h=>({...h,n:i(h)}));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(h=>h.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 Wf,bs=H(()=>{tn();Bb();Wf=new Set(["Bash"])});import Fb from"node:fs";import Gf from"node:fs/promises";import qf from"node:path";import AC from"node:os";async function Ub(e){try{return await Gf.realpath(e)}catch{return e}}function _C(e=EC){let t=[],n=r=>{let o;try{o=Fb.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=qf.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:Fb.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function IC(e,t=1048576){let n;try{n=await Gf.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(`
|
|
94
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function $C(e){let t=await IC(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 io(e,t){if(!e&&!t)return null;let n=_C();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Ub(e);for(let o of n){let s=await $C(o.file);if(s&&(s===e||await Ub(s)===r))return o}return null}async function Hb(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await io(e,t);if(!o)return[];let s;try{s=await At(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
95
|
+
`)){let p=d.trim();if(!p||p[0]!=="{")continue;let h;try{h=JSON.parse(p)}catch{continue}if((h.type==="turn_context"&&h.payload?.model||h.type==="session_meta"&&h.payload?.model)&&(i=h.payload.model),h.type==="event_msg"){let v=h.payload?.type;v==="task_started"?(l=!0,u=!1,f=h.payload?.turn_id||null):v==="task_complete"&&l&&(l=!1,!u&&h.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:co,sourceId:h.payload?.turn_id||f||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i}));continue}if(h.type!=="response_item")continue;let w=h.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,Kf.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let g=OC(w.content),b=c.length?c.slice():null;c=[],!(!g&&!b)&&a.push({role:"assistant",text:g,tools:b||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}function CC(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 MC(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+=`
|
|
96
|
+
`;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 NC(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=MC(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Wb(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await io(e,t);if(!o)return[];let s;try{s=await At(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
97
|
+
`)){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"&&Za.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let h=CC(p);h&&(f=[h])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=NC(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,h)=>{a.push({id:d?h?`${d}:${h}`:d:null,tool:u.name,command:p,description:"",summary:yr(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function qb(e,t){let n=await io(e,t);if(!n)return null;try{return Math.round((await Gf.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var EC,RC,OC,Za,Kf,co,lo=H(()=>{tn();bs();EC=process.env.BOSTRAT_CODEX_SESSIONS||qf.join(process.env.CODEX_HOME||qf.join(AC.homedir(),".codex"),"sessions");RC=new Set(["output_text","text"]),OC=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&RC.has(t.type)).map(t=>t.text).join(`
|
|
98
|
+
`).trim():"";Za=new Set(["exec_command","shell","exec"]),Kf=new Set(["wait","write_stdin"]),co="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 Qa({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Gb.has(r))return Gb.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 Kb(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Qa({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===co?"other":Qa({text:t}),code:null,status:null,requestId:null}}return null}function Vb(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Qa({text:n}),code:null,status:PC(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function PC(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var CG,Gb,yl=H(()=>{lo();CG=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Gb=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 DC(e,t){return!e||!t?null:en(e,t)}function BC(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(`
|
|
99
|
+
`).trim():""}function jC(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 LC(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function Vf(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 zb(e,t){if(!e.uuid)return null;let n=Vf(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 zf(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await At(e,t)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
100
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let w of l.content){let g=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof g=="string"&&FC.test(g)&&s.set(w.id,g.split("/").pop())}if(LC(l.content)){let w=zb(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=BC(l.content),d=u==="assistant"?jC(l.content):null,p=u==="user"?zb(c,s):null;if(!f&&!d&&!p)continue;let h=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:Qa({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:h||void 0})}return o.slice(-n)}async function Yb(e,t,{turns:n=16}={}){let r=DC(e,t);if(!r)return"";let o=await zf(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}:** ${UC(s.text||c,700)} ${i}`.trim()}).join(`
|
|
101
|
+
|
|
102
|
+
`):""}var FC,UC,bl=H(()=>{yl();tn();FC=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;UC=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import HC from"node:fs/promises";import{execFile as WC}from"node:child_process";import{promisify as qC}from"node:util";function Qb(e){Jf=typeof e=="function"?e:null}function Xf(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function ek({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Jb.has(r)||(Jb.add(r),Xb=Xb.then(()=>KC({sessionName:e,from:t,to:r})).catch(()=>{}))}async function KC({sessionName:e,from:t,to:n}){await ue({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await Yf("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await HC.realpath(r),s=Xf(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Yf("npm",["install","-g","@openai/codex"],{env:a,timeout:GC});let i=(await Yf(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await ue({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await Zb({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await ue({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await Zb({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function Zb(e){if(Jf)try{await Jf(e)}catch{}}var Yf,GC,Jb,Xb,Jf,kl=H(()=>{hn();Yf=qC(WC),GC=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Jb=new Set,Xb=Promise.resolve(),Jf=null});function zC(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
103
|
+
`);if(!tk.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(VC);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"&&tk.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 uo(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
104
|
+
`);for(let r=n.length-1;r>=0;r--)if(ok.test(n[r]))return n.slice(r).join(`
|
|
105
|
+
`);return t}function YC(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 JC(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 XC(e,t=""){let n=uo(e);if(ei.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
106
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(nk);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(nk.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&&!ok.test(r[l])&&YC(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=JC(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function eM(e,t=""){let n=uo(e);if(ei.test(n)||!ZC.test(n))return null;let r=n.trimEnd().split(`
|
|
107
|
+
`).pop()||"";return QC.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 nM(e,t=""){let n=uo(e);if(ei.test(n)||!rk.test(n))return null;let r=n.split(`
|
|
108
|
+
`),o=r.findIndex(i=>rk.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(tM);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 rM(e,t=""){let n=uo(e);return ei.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 oM(e,t=""){let n=uo(e);return ei.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 sM(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Zf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
109
|
+
`).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 iM(e,t=""){let n=String(e);if(!aM.test(n)||Zf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
110
|
+
`),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 cM(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Zf.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 sk(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=eM(t,n)||nM(t,n)||rM(t,n)||oM(t,n)||XC(t,n);if(r)return r}if(e==="cursor"){let r=iM(t,n)||cM(t,n)||sM(t,n);if(r)return r}return zC(t,{provider:e,box:n})}function ak(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 VC,tk,ei,ok,nk,ZC,QC,rk,tM,Zf,aM,xl=H(()=>{VC=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,tk=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;ei=/(^|\n)\s*›/,ok=/^\s*Welcome to Codex\b/;nk=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;ZC=/Successfully logged in\./,QC=/^\s*Press Enter to continue\.\s*$/;rk=/Enter this one-time code/,tM=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Zf=/Run Everything|Add a follow-up|Plan, search, build anything/;aM=/click this link to log in|Signing in with the browser/});function fM(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
111
|
+
`),n=-1;for(let s=t.length-1;s>=0;s--)if(t[s].trim()){n=s;break}if(n<0)return!1;let r=t[n];if(uM.test(r)||dM.test(r)){for(let s=n-1;s>=0&&n-s<=64;s--)if(/^\s*›/.test(t[s]))return!0}return/esc to interrupt|\bWorking \(/.test(String(e||""))}function gM(e){let t=String(e||"");if(!ze.codex.block.test(t))return null;let n=uo(t);if(!ze.codex.block.test(n))return null;let r=n.match(wM);return{from:r?.[1]||null,to:r?.[2]||null}}function wn(e){return new Promise(t=>setTimeout(t,e))}function Qf(e,t){let n=Date.now(),r=yM();for(let[s,a]of Sl)n-a>=r&&Sl.delete(s);let o=`${e}::${t}`;return Sl.has(o)?!1:(Sl.set(o,n),!0)}function uk(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
112
|
+
`),r=-1;for(let o=n.length-1;o>=0;o--)if(lk.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
113
|
+
`)}function kM(e){let t=0;for(let n of String(e).split(`
|
|
114
|
+
`))lk.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function ep(e,t){let n=ze[e];if(!n?.busy)return{state:"idle",reason:null};if(np(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(uk(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=kM(t);return e==="claude"&&r>0&&r<bM?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function tp(e,t){return ep(e,t).state}function dk(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
115
|
+
`).slice(-vM).join(`
|
|
116
|
+
`);return xM.test(n)}function xs(e,t){return tp(e,t)==="busy"}function Tl(e,t){return tp(e,t)!=="idle"}function vs(e,t){let n=ze[e];return n?.up?n.up.test(t):!0}function SM(e,t){let n=ze[e];return n?np(e,t)?!1:n.up.test(t)&&tp(e,t)==="idle":!0}function np(e,t){let n=ze[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 rp(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
117
117
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
118
|
-
`);return e}function
|
|
119
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
120
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
121
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
122
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
123
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
124
|
-
`):""}async function
|
|
118
|
+
`);return e}function fk(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function ik(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 ck(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await ao(e).catch(()=>{});;){let l=rp(await De(e),o);if(SM(t,l)){await s?.(),await wn(ks);return}if(ze[t]?.up&&!ze[t].up.test(l))if(await Bn(e)){if(c??=Date.now(),Date.now()-c>=Va()){let u=ik(t);throw ue({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,idleMs:Date.now()-c,error:u.message}).catch(()=>{}),u}}else c=null;else c=null;if(r&&t==="claude"&&hM.test(l)&&!ze.claude.up.test(l)){ue({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="codex"&&mM.test(l)&&!ze.codex.up.test(l)&&i<3){if(!Qf(e,"codex-trust")){await wn(vl);continue}i+=1,await ue({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await tt(e,"",{enter:!0,kind:"codex-trust-accept"}),await wn(ks);continue}if(t==="cursor"&&pM.test(l)&&!ze.cursor.up.test(l)&&i<3){if(!Qf(e,"cursor-trust")){await wn(vl);continue}i+=1,await ue({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await tt(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await wn(ks);continue}if(t==="codex"&&i<3){let u=gM(l);if(u){if(!Qf(e,`codex-update:${u.from}->${u.to}`)){await wn(vl);continue}i+=1,await ue({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await tt(e,"3",{enter:!1,kind:"codex-update-skip"}),ek({sessionName:e,from:u.from,to:u.to}),await wn(ks);continue}}if(Date.now()>a){if(np(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=ze.codex.auth.test(l)?"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"&&ze.cursor.auth.test(l)?u="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"&&ze.claude.auth.test(l)?u="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"&&ze.claude.block.test(l)&&(u='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'),ue({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await Bn(e)){let u=ik(t);throw ue({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u.message}).catch(()=>{}),u}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),ue({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!ze[t]?.block?.test(l),paneUp:!!ze[t]?.up?.test(l),paneBusy:!!ze[t]?.busy?.test(uk(ze[t],l))}).catch(()=>{}),await wn(ks);return}await wn(vl)}}function op({agent:e,agentCmd:t}={}){let n=t?hr(t):hr(e||"");return{label:n,realClaude:t?so(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 pk({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=op({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function ti({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}={}){let{realCodex:s,realCursor:a}=op({agent:n,agentCmd:r});return!t||!s&&!a?null:br({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).catch(()=>{})}async function br({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=lM(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:f,realCursor:d}=op({agent:r,agentCmd:o});if(u&&e&&await cr(e),f&&e&&await ts(e),d&&e&&await ns(e),(u||f||d)&&n){await ck(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});return}let p=ze[r]?r:ze[l]?l:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&p){await ck(n,p,s,{launchEcho:i});return}await wn(ks)}var lM,vl,ks,uM,dM,ze,pM,mM,hM,wM,yM,Sl,bM,lk,xM,vM,ni=H(()=>{ut();Ia();Wc();qc();kl();xl();hn();lM=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,vl=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ks=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,uM=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,dM=/^\s*tab to queue message\b/;ze={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyScope:"footer",busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:{test:fM},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/}},pM=/Trust this workspace|\[a\]\s*Trust/,mM=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,hM=/Resume from summary/i,wM=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;yM=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Sl=new Map;bM=68,lk=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;xM=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,vM=15});function _M(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
119
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=EM}function gn(e,t){return e==="codex"?{enter:!0,submitDelayMs:TM,..._M(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:AM}:{enter:!0}}var TM,AM,EM,ri=H(()=>{TM=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),AM=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),EM=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function oi(e,t){if(e==="codex")return CM(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
120
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(mk.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(mk.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(IM);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 CM(e){let t=String(e).replace(/\r/g,"").split(`
|
|
121
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){($M.test(t[o])||RM.test(t[o]))&&!sp.test(t[o])?n=o:sp.test(t[o])&&/\[Pasted Content \d+ chars\]/.test(t[o])&&(n=o+1);break}if(n<=0)return null;let r=[];for(let o=n-1;o>=0&&n-o<=60;o--){let s=t[o],a=s.match(sp);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(OM.test(s))return null;r.unshift(s)}return null}function ap(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Ve(n.replace(MM,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Ve(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Ve(e){return String(e||"").replace(/\s+/g," ").trim()}function hk(e,t,{slackMs:n=2e4}={}){let r=Ve(t?.text);if(!r||typeof t?.at!="number")return!1;let o=Array.isArray(e)?e:[],s=o.findIndex(a=>a?.role==="user"&&typeof a.ts=="number"&&a.ts>=t.at-n&&Ve(a.text).includes(r));return s>=0&&s<o.length-1}function wk(e,t,n,{busy:r=!1}={}){let o=oi(e,n);return o&&Al(t,o)||o&&e==="codex"&&ap(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Al(e,t){let n=Ve(e),r=Ve(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var mk,IM,sp,$M,RM,OM,MM,El=H(()=>{mk=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,IM=/^❯[ ]?(.*)$/,sp=/^\s*›[ ]?(.*)$/,$M=/\S+ · \S/,RM=/^\s*(?:\? for shortcuts|tab to queue message)\b/,OM=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;MM=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function ai({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say"},a={}){if(!NM.has(t))return!0;let i=a.waitForUp??pk({agent:t,agentCmd:n}),c=a.capture||(I=>De(I,{lines:et})),l=a.pressEnter||(I=>He(I,["Enter"],{kind:"submit-retry"})),u=a.sleep||jM,f=a.now||Date.now,d=a.log||(I=>ue(I).catch(()=>{})),p=a.maxMs??BM(),h=f()+p,w={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},g=0,b=0,v=0,x=!1;for(await u(si[0]);;){let I=await c(e).catch(()=>""),P=vs(t,I),B=xs(t,I),j=wk(t,r,I,{busy:B});if(j==="submitted")return!0;if(i&&(!P||j==="stuck"&&B)){if(f()>=h)return j==="stuck"?(d({...w,phase:"submit-failed",reason:"stuck-at-deadline",retries:g}),!1):(d({...w,phase:"submit-unverified",reason:x?"cli-down":"cli-never-up",retries:g,waitedMs:p}),!0);await u(PM);continue}if(x=!0,j==="stuck"){if(g>=DM)return d({...w,phase:"submit-failed",retries:g}),!1;g+=1,b=0,v=0,await l(e),d({...w,phase:"submit-retry",attempt:g}),await u(si[0]);continue}if(b+=1,b>=si.length||f()>=h)return!0;v+=1,await u(si[Math.min(v,si.length-1)])}}var NM,si,PM,DM,BM,jM,ip=H(()=>{ut();ni();El();hn();NM=new Set(["claude","codex"]),si=[250,450,700],PM=500,DM=2,BM=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},jM=e=>new Promise(t=>setTimeout(t,e))});function gk(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function Ln(e){let t=gk(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of LM){let o=0;for(let s of FM[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function yk(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))UM.has(r)&&(t+=o||0),HM.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function ii(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," ")}function bk(e){let t=gk(e).trim();return t?qM.test(t)?!0:t.split(/\s+/).length<=WM:!1}var LM,FM,UM,HM,WM,qM,_l=H(()=>{LM=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],FM={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 (it |this |that |them )?out\b/,/\btalk (me |us )?through\b/,/\b(give|show|tell) me\b/,/\bwhat about\b/,/\bwhy\b/,/\btake a look\b/,/\bwalk (me |us )?through\b/,/\bexplain\b/,/\bdo we (still |even )?need\b/,/\bis there (a|an|any)\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/,/\bkeeps? getting\b/,/\bdoesn'?t (seem|look) right\b/,/\bseems? (wrong|off|broken)\b/,/\b(too slow|slower than|much slower)\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/,/\bupdate (the |this |that |my |our )?[\w-]+( [\w-]+){0,3} to\b/,/\bset up\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/]};UM=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),HM=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);WM=12,qM=/^\s*(ok(ay)?|yes|yep|yeah|sure|go ahead|go|proceed|continue|next|begin|start|do it|do that|try again|try once more|again|ship it|perfect|great|good|nice|thanks|lock(ed)? (it |that |those )?in|option [a-z]\b|where are we|what'?s the status)\b/i});function Sk(e){let t=ii(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||GM.has(r)||n.push(r);return n}function KM(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(vk(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(vk(n,"assistant",o))}}return t}function vk(e,t,n){let r=Ln(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 ci(e){let t=KM(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Sk(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 Il(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(Sk(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[h,w]of f){let g=e.lengths[h],b=w+kk*(1-xk+xk*(g/(e.avgdl||1))),v=p*(w*(kk+1)/b);c.set(h,(c.get(h)||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 kk,xk,GM,cp=H(()=>{_l();kk=1.5,xk=.75,GM=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 $l from"node:fs/promises";import Tk from"node:path";async function VM(e){let t=Tk.join(e,".bostrat","events"),n;try{n=await $l.readdir(t)}catch{return ci([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await $l.readFile(Tk.join(t,o),"utf8");for(let a of s.split(`
|
|
122
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return ci(r)}function zM(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}
|
|
123
|
+
${o}`.trim()||t.slice(0,2e3)}function YM(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Il(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?["",`## ${Ak}, 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(`
|
|
124
|
+
`):""}async function Ek(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await $l.readFile(t,"utf8");if(a.includes(Ak))return{appended:!1,reason:"already-enriched"};let i=await VM(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=YM(i,zM(a),{k:r,maxChars:o,exclude:s});return c?(await $l.appendFile(t,`
|
|
125
125
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
126
|
-
- `).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
|
|
127
|
-
`).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
|
|
126
|
+
- `).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 Ak,_k=H(()=>{cp();Ak="Relevant prior discussion (verbatim, retrieved"});function tN(e){return JM.test(String(e||"").trimStart())}function Rk(e){let t=String(e||"");if(!tN(t.trimStart()))return null;let n=t.replace(XM,"").trim(),r=nN(n),o=[];for(let u of QM){let f=u.from.map(d=>Ss(r.get(d))).find(d=>d&&!Ik(d));f&&o.push({h:u.h,text:li(f,ZM)})}let s=Ss(r.get("Current Work"))||Ss(r.get("Primary Request and Intent"))||oN(n),a=Ol(s),i=li(Rl(dp(a[0]||"")),eN)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(li(Rl(dp(l)),up));for(let u of["Pending Tasks","Optional Next Step"]){let f=Ol(Ss(r.get(u)))[0]||"";if(!(!f||Ik(f))){c.push(li(`Left to do: ${Rl(dp(f))}`,up));break}}if(!c.length){let u=Ol(Ss(r.get("Problem Solving"))||"")[0];u&&c.push(li(Rl(u),up))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function nN(e){let t=new Map,n=[];lp.lastIndex=0;for(let r=lp.exec(e);r;r=lp.exec(e))n.push({name:rN(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 rN(e){return $k.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Ss(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
127
|
+
`).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 oN(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Ss(t.split(/\n{2,}/).slice(0,2).join(" "))}function Ol(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 dp(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 Rl(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Ik(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function li(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Ol(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 JM,$k,lp,XM,ZM,QM,eN,up,Ok=H(()=>{JM=/^This session is being continued from a previous conversation that ran out of context\./,$k=["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"],lp=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${$k.join("|")})[:*\t ]*$`,"gim"),XM=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,ZM=420,QM=[{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"]}],eN=116,up=168});function Mk(e){return fp.has(String(e||"").replace(/^\//,"").toLowerCase())}function Ts(e){let t=String(e||"").trim();if(!t)return null;let n=Rk(t);if(n)return n;let r=t.match(sN);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(aN);if(o)return{kind:"skill",name:o[1]};let s=t.match(iN);if(s){let i=s[1].toLowerCase();return fp.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(gN);if(a&&fp.has(a[1].toLowerCase()))return{kind:"suppress"};if(cN.test(t))return{kind:"suppress"};if(uN.test(t))return{kind:"suppress"};if(lN.test(t))return{kind:"suppress"};if(dN.test(t)){let i=l=>yN(t.match(l)?.[1].trim()||"")||null,c=i(wN);return{kind:"task",summary:i(fN)||"",taskId:i(pN),toolUseId:i(mN),status:i(hN)?.toLowerCase()||null,resultHead:c?c.length>Ck?`${c.slice(0,Ck).trimEnd()}\u2026`:c:null}}return null}function yN(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Nk(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 Pk(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var sN,aN,iN,cN,lN,uN,dN,fN,pN,mN,hN,wN,Ck,fp,gN,Cl=H(()=>{Ok();sN=/^Base directory for this skill:\s*(\S+)/,aN=/^Run the "([^"]+)" workflow\./,iN=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,cN=/^<local-command-(?:stdout|caveat)>/i,lN=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,uN=/^Continue from where you left off\.$/,dN=/^<task-notification>/i,fN=/<summary>([\s\S]*?)<\/summary>/i,pN=/<task-id>([\s\S]*?)<\/task-id>/i,mN=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,hN=/<status>([\s\S]*?)<\/status>/i,wN=/<result>([\s\S]*?)(?:<\/result>|$)/i,Ck=600,fp=new Set(["model","clear","compact","login","exit","quit"]);gN=/^\/([a-z0-9:_-]+)$/i});import bN from"node:crypto";function SN(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,Bk)){let o=ui(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,kN)){let c=ui(i?.label);if(!c)return null;let l=ui(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<xN)return null;n.push({question:o,header:ui(r?.header).slice(0,vN),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function pp(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Dk.lastIndex=0;let t=null;for(let o;o=Dk.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=SN(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function mp(e,t,n){let r=bN.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function hp(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Bk}={}){if(!Array.isArray(e)||!t||!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||pp(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=mp(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 jk(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=ui(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
128
128
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
129
129
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
130
|
-
${o}`),s||""}var
|
|
130
|
+
${o}`),s||""}var Bk,kN,xN,vN,Dk,ui,Lk,wp=H(()=>{Bk=4,kN=4,xN=2,vN=24,Dk=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,ui=e=>typeof e=="string"?e.trim():"";Lk=`
|
|
131
131
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
132
132
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
133
133
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -147,76 +147,76 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
147
147
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
148
148
|
anything you can decide yourself; keep asking in prose for questions without
|
|
149
149
|
enumerable options.
|
|
150
|
-
`});function RM(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 CM(e,t,n){let r=ck(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 wr(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,m)=>{u.set(y,m),t.push(m)},d=y=>{let m=u.get(y);m&&(m.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,h=new Map;for(let y of e||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let m=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let b of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let M=b[0].split("/").pop().toLowerCase();h.has(M)||h.set(M,[]),h.get(M).push(y.payload?.ts??y.ts)}}let w=3*6e4,g=20*6e4,k=(y,m)=>!!(y?.name&&h.get(String(y.name).toLowerCase()))?.some(M=>M>=m-w&&M<=m+g);for(let y of e||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let m=We(y.payload?.text);m&&(o.has(y.boxId)||o.set(y.boxId,[]),o.get(y.boxId).push(m))}else if(y.type==="turn.user"&&y.source==="orchestrator"){let m=We(y.payload?.text);m&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(m))}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 m=y.payload.note,b={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:fe(y)};m.toolUseId&&i.set(m.toolUseId,b);let M=String(m.summary||"").match(p);M&&l.set(M[1],b)}let S=new Map,E=new Map,R=null,B=new Map,T=new Map,_=new Map,O=new Map,I=new Map,U=new Map,H=new Map,ae=new Map,X=new Map,C=new Map,Q=new Map,ve=new Map,W=new Map,be=new Map,se=new Map,pe=[],ie=y=>pe.push(y),L=y=>{if(!pe.length)return;let m=fe(y);for(let b of pe){if(b.status!=="open"||b.box&&b.box!==y.boxId||(b.ts??0)>m)continue;let M=t.indexOf(b);M>=0&&t.splice(M,1),b.ts=m,t.push(b)}},ce=new Map,ue=new Map,Ae=new Map,_e=new Map,xt=new Map,je=new Map,A=()=>{for(let y of je.values())y.questions.length&&t.push(y);je.clear()},G=null,Ge=6e4,_t=new Map,at=(y,m)=>{let b=o.get(y);if(!b?.length)return!1;let M=We(m),ne=s.get(y);return b.some(de=>{if(!M.startsWith(de))return!1;let De=We(M.slice(de.length));return!!De&&!!ne?.has(De)})};for(let y of e||[]){let m=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||(pe.length=0,!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length))break;{let b=m.note||El(m.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let M=String(b.summary||"").match(p);if(!b.toolUseId&&M&&c.has(M[1]))break}if(b.kind==="command"&&tk(b.command))break;if(b.kind==="resume-summary"){t.push({id:y.id,kind:"resume-summary",title:b.title||"Context resumed from summary",bullets:b.bullets||[],sections:b.sections||[],rawChars:b.rawChars??null,ts:fe(y),box:y.boxId,agent:y.provider});break}b.kind!=="suppress"&&t.push({id:y.id,kind:"event",type:rk(b),name:b.name??null,command:b.command??null,summary:b.summary??null,status:b.status??null,ts:fe(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${fe(y)}`)||y.source==="transcript"&&at(y.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){t.push({id:y.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:fe(y),box:y.boxId,agent:y.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"){m.kind==="prompt-answer"&&(G=null);break}for(let b of B.values())b.status="stale";B.clear();for(let b of _.values())b.status="stale";_.clear();for(let b of W.values())b.status="stale";W.clear(),G&&G.box===y.boxId&&(t.push(RM(G)),G=null),t.push({id:y.id,kind:"message",role:"user",text:m.text,ts:fe(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...y.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let b=m.from||{};t.push({id:y.id,kind:"event",type:"peer-message",text:m.text,from:b.trackName||b.boxId||b.name||null,fromTrackId:b.trackId||null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let b=gb(m,{codexFailedText:ao});if(b){t.push({id:y.id,kind:"event",type:"provider-error",cls:b.cls,status:b.status??void 0,text:b.text,provider:y.provider,legacy:!0,at:fe(y),ts:fe(y),box:y.boxId,agent:y.provider});break}A();let M=(m.images||[]).filter(Re=>!k(Re,fe(y)));if(!String(m.text||"").trim()&&!M.length)break;if(y.provider==="cursor"){let Re=`${y.boxId}|${m.text}`;if(n.has(Re))break;n.add(Re)}(!G||G.box===y.boxId)&&(G=null);let ne=y.provider==="codex"?(m.tools||[]).filter(Re=>!Af.has(Re)&&!za.has(Re)):m.tools||[],de=m.text,De=null,ht=zf(String(m.text||""));ht&&(de=ht.text,De={questions:ht.questions}),t.push({id:y.id,kind:"message",role:"assistant",text:de,ts:fe(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...De?{ask:De}:{},...ne.length?{tools:ne}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...M.length?{images:M}:{}}),L(y),De&&(G={id:y.id,groupId:Yf(y.provider,m.sourceId||y.id,De.questions),provider:y.provider,box:y.boxId,questions:De.questions,ts:fe(y)});break}case"job.started":{if(!m.id)break;let b=fe(y),M=i.get(m.id);if(!M&&(m.kind||"agent")==="agent"&&m.name){let Re=l.get(m.name);Re&&Re.ts>=b&&(M=Re)}let ne={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:M?M.status==="killed"?"stopped":M.status&&M.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:M?.ts??null,summary:M?.summary??null,resultHead:M?.resultHead??null},de=_t.get(y.boxId),De=de?.jobs[de.jobs.length-1]?.startedTs;if(de&&De!=null&&b-De<=Ge)de.jobs.push(ne);else{let Re={id:y.id,kind:"job-dock",dockId:m.id,ts:b,box:y.boxId,agent:y.provider,jobs:[ne]};_t.set(y.boxId,Re),t.push(Re)}break}case"handoff.done":t.push(dt(y,"lead-handoff"));break;case"subtask.delegated":t.push({...dt(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":t.push(dt(y,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let M of B.values())M.status="stale";B.clear();for(let M of _.values())M.status="stale";_.clear()}let b=dt(y,`pr-${m.change}`);if(b.box=y.boxId??null,OM.has(m.change)&&m.number!=null){let M=CM(S,E,m);if(S.size>=2){for(let de of E.values())for(let De of de)De.grouped=!0;b.groupPrs=[...S.values()];let ne=[b];for(let de of S.keys())E.set(de,ne)}else{let ne=E.get(M)??[];ne.push(b),E.set(M,ne)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let M=ck(m),ne=R&&!R.complete||S.size>=2;if(S.has(M))S.delete(M),E.delete(M);else if(m.number!=null)for(let de of[...S.keys()])de.endsWith(`#${m.number}`)&&(S.delete(de),E.delete(de));if(ne&&m.number!=null){(!R||R.complete)&&(R={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:fe(y),ts:fe(y),box:y.boxId,agent:y.provider},t.push(R)),R.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:fe(y)}),R.remaining=[...S.values()],R.complete=S.size===0;break}}t.push(b);break}case"pr.offered":{if(!m.offerId)break;for(let M of B.values())M.status="stale";B.clear();for(let M of _.values())M.status="stale";_.clear();let b={id:y.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:y.source==="cli"||m.via==="cli"?"cli":"transcript",ts:fe(y),box:y.boxId,agent:y.provider};B.set(m.offerId,b),T.set(m.offerId,b),t.push(b),b.source==="cli"&&ie(b);break}case"pr.offer.answered":{let b=T.get(m.offerId);b&&(b.status=m.action||"dismiss",b.actions=Array.isArray(m.actions)?m.actions:null,b.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(b.existingPr={number:m.existingPr.number??null}),B.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let M of _.values())M.status="stale";_.clear();let b={id:y.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};_.set(m.offerId,b),O.set(m.offerId,b),t.push(b);break}case"pr.exists.answered":{let b=O.get(m.offerId);b&&(b.status=m.choice||"dismiss",_.delete(m.offerId));break}case"box.state.changed":{let b=m.change==="model"?"model-change":m.change;if(!b)break;let M=dt(y,b);if(b==="model-change"&&(M.to=m.model),b==="node-update"&&M.latest){let ne=t.findIndex(de=>de.kind==="event"&&de.type==="node-update"&&de.latest===M.latest);if(ne>=0){let de=t[ne];t[ne]={...M,ts:de.ts,at:de.at,firstAt:de.firstAt??de.ts,updatedAt:M.ts};break}}t.push(M);break}case"track.transferred":case"track.recovered":t.push(dt(y,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){d(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;f(`view:${m.viewId}`,{id:y.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:fe(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{t.push({id:y.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:fe(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(m.change==="picked"){t.push({id:y.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:fe(y),box:y.boxId,agent:y.provider});break}if(m.change==="deleted"){d(`mock:${m.mockId}`);break}if(m.change!=="created")break;f(`mock:${m.mockId}`,{id:y.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(m.change==="deleted"){d(`flow:${m.flowId}`);break}if(m.change!=="created")break;f(`flow:${m.flowId}`,{id:y.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:fe(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){d(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;f(`runbook:${m.runbookId}`,{id:y.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:fe(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{t.push({id:y.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let M of H.values())M.box===(m.box||y.boxId)&&(M.status="stale",H.delete(M.offerId));let b={id:y.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:fe(y),box:m.box||y.boxId,agent:m.agent||y.provider};H.set(m.offerId,b),ae.set(m.offerId,b),t.push(b);break}case"box.fullscreen.answered":{let b=ae.get(m.offerId);b&&(b.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",H.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let M of I.values())M.key===m.key&&(M.status="stale",I.delete(M.offerId));let b={id:y.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};I.set(m.offerId,b),U.set(m.offerId,b),t.push(b);break}case"ticket.move.answered":{let b=U.get(m.offerId);b&&(b.status=m.action==="moved"?"moved":"dismiss",I.delete(m.offerId));break}case"ticket.state.changed":{for(let b of I.values())b.key===m.key&&b.to===m.to&&(b.status="stale",I.delete(b.offerId));if(m.mode==="offer")break;t.push(dt(y,"ticket-state"));break}case"ticket.commented":{t.push({id:y.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":t.push(dt(y,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let M of X.values())M.status="stale";X.clear();let b={id:y.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};X.set(m.proposalId,b),C.set(m.proposalId,b),t.push(b),ie(b);break}case"ticket.create.answered":{let b=C.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",b.createdCount=m.createdCount??null,b.failedCount=m.failedCount??null,X.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let M of Q.values())M.status="stale";Q.clear();let b={id:y.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};Q.set(m.proposalId,b),ve.set(m.proposalId,b),t.push(b),ie(b);break}case"loop.create.answered":{let b=ve.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",Q.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let b=m.replaces?se.get(m.replaces):null;b&&b.status==="armed"&&(b.status="replaced");let M={id:y.id,kind:"followup-armed",armId:m.armId,trigger:m.trigger??"now",label:m.label??"",prompt:m.prompt??"",why:m.why??null,status:"armed",ts:fe(y),box:y.boxId,agent:y.provider};se.set(m.armId,M),M.trigger!=="now"&&t.push(M);break}case"followup.armed.cancelled":{let b=se.get(m.armId);b&&b.status==="armed"&&(b.status="cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let ne of W.values())ne.trigger===m.trigger&&(ne.status="stale",W.delete(ne.offerId));let b=Array.isArray(m.rows)?m.rows:[];for(let ne of b){let de=ne.armId?se.get(ne.armId):null;de&&de.status==="armed"&&(de.status="offered")}let M={id:y.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},rows:b.map(ne=>({followupId:ne.followupId??null,armId:ne.armId??null,label:ne.label??"",prompt:ne.prompt??null,promptPreview:ne.promptPreview??null,promptHash:ne.promptHash??null,origin:ne.origin??"operator",why:ne.why??null})),status:"open",answer:null,ts:fe(y),box:y.boxId,agent:y.provider};W.set(m.offerId,M),be.set(m.offerId,M),t.push(M),M.trigger==="now"&&ie(M);break}case"followup.answered":{let b=be.get(m.offerId);b&&(b.status=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss",b.answer={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0},W.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let M of ce.values())M.status="stale";ce.clear();let b={id:y.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:fe(y),box:y.boxId,agent:y.provider};ce.set(m.proposalId,b),ue.set(m.proposalId,b),t.push(b),ie(b);break}case"followup.create.answered":{let b=ue.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",ce.delete(m.proposalId));break}case"followup.created":{let b=m.proposalId?ue.get(m.proposalId):null;b&&(b.created={followupId:m.followupId??null});break}case"loop.created":{let b=m.proposalId?ve.get(m.proposalId):null;b&&(b.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(y,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let ne of Ae.values())ne.loopId===m.loopId&&(ne.status="stale",ne.staleReason="superseded",Ae.delete(ne.proposalId));let b=y.type==="loop.update.offered",M={id:y.id,kind:b?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...b?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:fe(y),box:y.boxId,agent:y.provider};Ae.set(m.proposalId,M),_e.set(m.proposalId,M),t.push(M),ie(M);break}case"loop.update.answered":{let b=_e.get(m.proposalId);b&&(m.action==="moved"?(b.status="stale",b.staleReason="moved"):b.status=m.action==="applied"?"applied":"dismiss",Ae.delete(m.proposalId));break}case"loop.delete.answered":{let b=_e.get(m.proposalId);b&&(m.action==="moved"?(b.status="stale",b.staleReason="moved"):b.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",Ae.delete(m.proposalId));break}case"loop.updated":{let b=m.proposalId?_e.get(m.proposalId):null;b&&(b.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(y,"loop-updated"));break}case"loop.deleted":t.push(dt(y,"loop-deleted"));break;case"loop.paused":t.push(dt(y,"loop-paused"));break;case"rework.cause.offered":{if(!m.sha||xt.has(m.sha))break;let b={id:y.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,status:"open",cause:null,ts:fe(y),box:y.boxId,agent:y.provider};xt.set(m.sha,b),t.push(b);break}case"rework.cause.answered":{let b=m.sha?xt.get(m.sha):null;b&&(b.status="answered",b.cause=m.cause??null);break}case"ticket.created":{let b={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},M=m.proposalId?C.get(m.proposalId):null;if(M){M.created=b;break}t.push({id:y.id,kind:"ticket-created",...b,ts:fe(y),box:y.boxId,agent:y.provider});break}case"error":{if(!m.kind)break;t.push(dt(y,m.kind));break}case"provider.error":t.push({...dt(y,"provider-error"),provider:y.provider,box:y.boxId,agent:y.provider});break;case"usage.cap.hit":t.push(dt(y,"usage-cap-hit"));break;case"usage.cap.warning":t.push(dt(y,"usage-cap-warning"));break;case"provider.connected":{t.push({id:y.id,kind:"event",type:"provider-connected",provider:m.provider||y.provider,ts:fe(y),at:fe(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(G=null,m.control||!m.question)break;let b=m.groupId||m.promptId;if(!b)break;let M=je.get(b);M||(M={id:y.id,kind:"prompt",groupId:b,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:m.questionTotal??1,questions:[],ts:fe(y)},je.set(b,M));let ne=m.questionIndex??0,de={index:ne,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},De=M.questions.findIndex(Re=>Re.index===ne&&We(Re.question)===We(de.question));De>=0?M.questions[De]=de:M.questions.push(de),M.questions.sort((Re,ya)=>Re.index-ya.index),M.questionTotal=Math.max(M.questionTotal,m.questionTotal??1),M.questions.length>=M.questionTotal&&(t.push(M),je.delete(b));break}default:break}}let nt=u.size?t.filter(y=>!y.retired):t;return NM(nt.sort((y,m)=>(y.ts??0)-(m.ts??0)))}function NM(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<=MM){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 Zf(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:PM(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??wr(e)).filter(o=>o.kind==="message");return lb(n,r)}function PM(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ps(e.command)?t:mr(e.command,e.description||"")}function dt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:fe(e),ts:fe(e)}}function lk(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)+DM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&BM.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function uk(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!LM.has(r.type)||!r.box)continue;let o=(r.ts??0)+jM,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 dk(e){return(e||[]).filter(t=>FM.has(t?.kind)&&t.status==="open")}var fe,OM,ck,MM,DM,BM,jM,LM,FM,Qf=j(()=>{ms();io();dl();bl();Vf();Xf();fe=e=>e.payload?.ts??e.ts;OM=new Set(["opened","ci-fixed","reconciled","pushed"]),ck=e=>`${e.repo??""}#${e.number}`;MM=2*6e4;DM=12e4,BM=new Set(["archived","unarchived","opened"]);jM=2500,LM=new Set(["pr-opened","pr-merged","pr-auto-armed"]);FM=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","fullscreen-offer","followup-create-offer"])});function KM(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 VM(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 zM(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<WM&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<qM&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function hk(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=KM(e),s=VM(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=wr(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-UM);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(mk[r]??mk.failover),l.push(""),o.lastPush?.sha){let d=pk(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=pk(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=zM(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"}:** ${GM(d.text,HM)}`),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(`
|
|
151
|
-
`);return f.length<=
|
|
152
|
-
|
|
153
|
-
[\u2026 brief truncated]`}var
|
|
154
|
-
|
|
155
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function oN(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(bk.test(s)){if(r.length>=Rk)continue;let a=rN(s.replace(bk,"").replace(/[.;,:]+$/,""),Ok,tN);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>yk&&(o=`${o.slice(0,yk-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Ck(e,t,{timeoutMs:n=9e4}={}){let r=yr.join(t,QM),o=yr.join(r,eN);await gr.mkdir(r,{recursive:!0});let s=await Al(o),a=await ge(e);if(!a)throw new Error(`session "${e}" not found`);return await _l(e,a,nN(o)),await Lk(o,s,n),oN(await gr.readFile(o,"utf8"))}function Mk(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Ak||n===$k;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(Ek)||n===JM||r.startsWith(_k)||r.startsWith(Ik)}function Nk(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-sN;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 Pk(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 Dk(e,{windows:t=[]}={}){if(Mk(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 Il(e){return ep.get(e)||null}async function Bk(e){let t=ep.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ge(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:yr.join(n.cwd,vk,Sk)};return ep.set(e,r),cN(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function cN(e,t){e.step="summarize",await jk(e.name,t),e.step="clear";let n=await ge(e.name);await _l(e.name,n,"/clear"),await rp(aN),e.step="resume",await _l(e.name,n,ZM),e.step="done",e.finishedAt=Date.now()}async function jk(e,t,{timeoutMs:n=iN}={}){let r=yr.join(t,vk),o=yr.join(r,Sk);await gr.mkdir(r,{recursive:!0});let s=await Al(o),a=await ge(e);await re({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await _l(e,a,XM(o)),await Lk(o,s,n),await re({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Al(o)});let i=await zb(t,o,{exclude:u=>Mk(u.role,u.text)});await re({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 gr.readFile(o,"utf8");await $({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Tk(u),ts:Date.now()}})}catch{}}await Qd(t);let l=await lN(r,o);return c&&await Oy(c,l||o,{box:e,agent:a?.agent??null}),o}async function tp(e,{timeoutMs:t}={}){let n=await ge(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return jk(e,n.cwd,t?{timeoutMs:t}:void 0)}async function np(e,t,{trackName:n="this Track"}={}){let r=await F({id:e,cwd:t}),o=hk(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=yr.join(t,kk);return await gr.mkdir(yr.dirname(s),{recursive:!0}),await gr.writeFile(s,o,"utf8"),await $({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Tk(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),kk}async function Al(e){try{return(await gr.stat(e)).mtimeMs}catch{return 0}}async function Lk(e,t,n){let r=Date.now()+n;for(;;){if(await Al(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await rp(2e3)}}async function lN(e,t){try{if(!YM.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=yr.join(e,`${n}.md`);return await gr.copyFile(t,r),r}catch{return null}}function rp(e){return new Promise(t=>setTimeout(t,e))}function xk(e){return e==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":""}async function _l(e,t,n){if(t){let o=xk(t.agent);await hr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ge(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Le(e,["Escape"],{kind:"drive-wipe"}),await rp(150),await ls(e,cl,{kind:"drive-wipe"}),await Ze(e,n,{kind:"orchestration-drive",...pn(r.agent,n)}),await ni({sessionName:e,agent:r.agent,agentCmd:xk(r.agent),text:n,flow:"orchestration-drive"})}var vk,Sk,gk,Ek,Ak,_k,JM,XM,ZM,Ik,$k,QM,eN,yk,Rk,Ok,tN,nN,bk,rN,sN,aN,iN,ep,kk,op=j(()=>{lt();Za();dn();Qa();Uf();Yb();Oa();me();Qc();wk();vk=".bostrat/handoff",Sk="latest.md",gk=32*1024;Ek="Checkpoint: write a work-in-progress handoff",Ak="HANDOFF WRITTEN",_k="Resume from .bostrat/handoff/latest.md",JM="/clear",XM=e=>`${Ek} 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 ${Ak} and take no further action.`,ZM=`${_k}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Ik="Beat: write a",$k="BEAT WRITTEN",QM=".bostrat/arc",eN="beat.txt",yk=200,Rk=4,Ok=5,tN=36,nN=e=>`${Ik} 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 ${Rk} further lines, each starting with "- " and naming ONE piece in at most ${Ok} 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 ${$k} and take no further action.`,bk=/^(?:[-–—*•·]|\d+[.)])\s+/,rN=(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};sN=6e5;aN=3500,iN=3e5,ep=new Map;kk=".bostrat/handoff/status-latest.md"});function kr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!$l.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 Hk(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:kr(o.from,"from",n),toMs:kr(o.to,"to",n)}))}return t}function ap(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 dN(e){if(!e)return null;let t=String(e).trim();return br[t]?t:Fk[t]?Fk[t]:Object.keys(br).find(r=>t.startsWith(r))||null}function fN(e){if(!e||e===br)return uN;let t=Uk.get(e);return t||(t=Hk(e),Uk.set(e,t)),t}function Wk(e,t,n={}){let{table:r,speed:o}=n,s=fN(r),a=r&&r!==br?s[e]?e:null:dN(e);if(!a)return null;let i=ap(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function mn(e,t={},n,r){let o=Wk(e,n,r);return o?((t.input||0)*o.input+(t.output||0)*o.output+(t.cacheWrite||0)*o.cacheWrite+(t.cacheRead||0)*o.cacheRead)/1e6:0}function pN(e=br){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:kr(a.from,"from",n),toMs:kr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),$l.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`),a.fast&&(typeof a.fast[i]!="number"||!Number.isFinite(a.fast[i])||a.fast[i]<0)&&t.push(`${n}: fast variant ${i} must be a non-negative number, got ${JSON.stringify(a.fast?.[i])}`)}}return t}function hN({now:e=Date.now(),table:t=br,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...mN,...n},a=[],i=ap(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(f=>({...f,fromMs:kr(f.from,"from",c),toMs:kr(f.to,"to",c),verifiedMs:$l.test(String(f.verifiedAt??""))?Date.parse(`${f.verifiedAt}T00:00:00.000Z`):NaN}))}catch(f){a.push(String(f.message));continue}for(let f of u){if(!Number.isFinite(f.verifiedMs)){a.push(`${c}: window ${f.from??"\u2026"} \u2192 ${f.to??"now"} has no verifiedAt`);continue}let d=i>=f.fromMs&&i<=f.toMs,p=f.fromMs>i&&f.fromMs-i<=o*sp;d&&i-f.verifiedMs>r*sp&&a.push(`${c}: current rate last verified ${f.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),p&&f.fromMs-f.verifiedMs>s*sp&&a.push(`${c}: scheduled rate change on ${f.from} was last verified ${f.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function bs({now:e=Date.now(),table:t=br,...n}={}){let r=ap(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=kr(l.from,"from",i),f=kr(l.to,"to",i);r>=u&&r<=f&&$l.test(String(l.verifiedAt??""))&&(!o||l.verifiedAt<o)&&(o=l.verifiedAt),u>r&&s.push({model:i,from:l.from,verifiedAt:l.verifiedAt??null})}s.sort((i,c)=>i.from<c.from?-1:1);let a=[...pN(t),...hN({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var br,Fk,$l,uN,Uk,qk,mN,sp,Dn=j(()=>{br={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Fk={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"},$l=/^\d{4}-\d{2}-\d{2}$/;uN=Hk(br);Uk=new WeakMap;qk=(e,t,n)=>Wk(e,t,n)!==null;mN=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),sp=864e5});function wN(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Gk(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Ft(e){let t=e.payload?.model||"unknown",n=wN(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=mn(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!qk(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function xr(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=Kk();for(let d of n){let{usd:p,model:h,tokens:w,repriced:g,unpriced:k,asRecorded:S}=Ft(d);i+=S;let E=d.provider||"unknown",R=d.boxId||"unattributed";if(g&&c++,k&&(l++,u.add(h)),a+=p,Gk(f,w),r[h]||={usd:0,tokens:Kk(),events:0},r[h].usd+=p,r[h].events+=1,Gk(r[h].tokens,w),o[E]||={usd:0,events:0},o[E].usd+=p,o[E].events+=1,s[R]||={usd:0,events:0,provider:E,models:new Set,label:t[R]||R},s[R].usd+=p,s[R].events+=1,h!=="unknown"&&s[R].models.add(h),d.epoch!=null){let B=(s[R].epochs||={})[d.epoch]||={usd:0,events:0,provider:E,models:new Set};B.usd+=p,B.events+=1,B.provider=E,h!=="unknown"&&B.models.add(h)}}return{totalUsd:ks(a),asRecordedUsd:ks(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Rl(r,d=>({...d,usd:ks(d.usd)})),byProvider:Rl(o,d=>({...d,usd:ks(d.usd)})),byBox:Rl(s,d=>({usd:ks(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Rl(d.epochs,p=>({usd:ks(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Kk,ks,Rl,vr=j(()=>{Dn();Kk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});ks=e=>Math.round(e*1e4)/1e4,Rl=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function gN(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 Vk(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(ip(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(ip(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(`${ip(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=xr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?gN(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var ip,zk=j(()=>{vr();ip=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as yN}from"node:child_process";import{promisify as bN}from"node:util";import{randomUUID as kN}from"node:crypto";async function cp(e,t){let n=Date.now()+t;for(;;){if(await Mn(e))return!0;if(Date.now()>n)return!1;await ii(vN)}}async function TN(e,t){let n=Jk[t]||Jk.default;n.escape&&(await Le(e,["Escape"],{kind:"swap-exit"}),await ii(150)),n.wipe&&await ls(e,cl,{kind:"swap-exit"}),await ll(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=pn(t).submitDelayMs||0;await ii(Math.max(r,250)),await Le(e,["Enter"],{kind:"swap-exit"})}async function Xk(e,t){try{let{stdout:n}=await lo(K,V("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Zk(e){await lo(K,V("respawn-pane","-k","-t",e))}async function lp(e,t={}){if(!Pe(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ge(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||dr(n),a=Number(t.exitTimeoutMs)||xN;await re({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Mn(e)&&(await TN(e,o),!await cp(e,a)&&(i=!0,await re({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Zk(e),!await cp(e,Yk))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&ro(n),l=c?Nc():null,u=await Xk(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await lo(K,V("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await lo(K,V("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?nl(g,l):g}if(f&&!i&&(await Zk(e),!await cp(e,Yk)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,p=t.resumeSessionId||kN(),h=d?n:Ua(n,p,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await or(r.cwd),s==="codex"&&await Jo(r.cwd),s==="cursor"&&await Xo(r.cwd),await oo(e),await lo(K,V("set-option","-t",e,"@bostrat-sid",p)),await lo(K,V("set-option","-t",e,"@bostrat-agent",s)),await ii(SN),c&&Fa().length&&(await Xk(e,La)||await lo(K,V("set-environment","-t",e,La,"1")).catch(()=>{}),await Ze(e,`export ${La}=1`,{kind:"swap-env"}),await ii(150)),await Ze(e,h,{kind:"swap-launch"}),await re({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:f,launchCmd:h}}var lo,ii,xN,Yk,vN,SN,Jk,Qk=j(()=>{lt();Ta();Dc();Bc();Qa();dn();lo=bN(yN),ii=e=>new Promise(t=>setTimeout(t,e)),xN=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Yk=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,vN=250,SN=400,Jk={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 e0(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Et(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
156
|
-
`)){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?
|
|
157
|
-
`).trim():"",
|
|
158
|
-
`),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(
|
|
159
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
160
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
161
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
162
|
-
`),o=
|
|
163
|
-
`),o=
|
|
164
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
165
|
-
`)].reverse().find(a=>
|
|
166
|
-
`),o=
|
|
167
|
-
`),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(
|
|
168
|
-
`),o=
|
|
169
|
-
`).pop()||"")?!0:
|
|
170
|
-
`).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
|
|
171
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
172
|
-
`).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
|
|
173
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
174
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
175
|
-
`),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
|
|
176
|
-
`),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
|
|
177
|
-
`);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
|
|
178
|
-
`),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 ox(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&&!tx.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=nx.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!TP.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function pi(e,t,{timeoutMs:n=vP}={}){let r=Date.now()+n;for(;;){let o=await Ce(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Ul(SP)}}async function _P(e,t){return await Ze(e,`/model ${t.model}`,pn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function IP(e,t){await il(e),await Ze(e,"/model",{enter:!1}),await Ul(250),await Le(e,["Enter"],{kind:"model-switch"});let n=await pi(e,X0);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Le(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=AP(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Ze(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await pi(e,i=>Z0(i)||!X0(i)&&o(i)&&{done:o(i)});if(!s)throw await Le(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?tx.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 Le(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Ze(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await pi(e,i=>!Z0(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 $P(){let e=J0.join(process.env.CURSOR_CONFIG_DIR||J0.join(xP.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await kP.readFile(e,"utf8"))?.model||null}catch{return null}}async function RP(e,t){await il(e),await ll(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await pi(e,Q0),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 pi(e,Q0,{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 Le(e,["Down"],{kind:"model-switch"}),await Ul(250)}if(!n||!s||!o(s)){await Le(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Le(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 Le(e,["Enter"],{kind:"model-switch"}),await Ul(800);let a=await $P();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 sx(e,t,n){return e==="codex"?IP(t,n):e==="cursor"?RP(t,n):_P(t,n)}var Ul,vP,SP,ex,TP,EP,tx,nx,ax=j(()=>{lt();Qa();Ul=e=>new Promise(t=>setTimeout(t,e)),vP=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,SP=300,ex=[{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"}],TP=new Set([...ex.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"]),EP=[{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"}],tx=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),nx=[{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 Wl(e){let t=e||{},n=OP(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function OP(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:gt(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:gt(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:gt(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:gt(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:gt(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:gt(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:gt(e.agent),payload:{...Er(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:gt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Er(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{change:"subtask-resumed",...Er(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",...Er(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Er(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Er(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{...Er(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Er(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function lx(e,t){let n=Wl(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Up(()=>$(r,n))}async function ux(e){if(!(!e?.id||ix.has(e.id))){ix.add(e.id);try{let t=await F(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=Wl(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(e,o)}}catch{}}}function Er(e){let{type:t,at:n,...r}=e;return r}function dx(e,t,n){!n?.promptId||Fp.get(e.id)===n.promptId||(Fp.set(e.id,n.promptId),Up(()=>$(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:gt(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 Hp(e,t,n,r={}){Fp.delete(e.id),Up(()=>$(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:gt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var gt,Hl,Up,cx,ix,Fp,Wp=j(()=>{me();gt=e=>e||null;Hl=Promise.resolve(),Up=e=>(Hl=Hl.then(e).catch(()=>{}),Hl),cx=()=>Hl;ix=new Set;Fp=new Map});function DP(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function gx(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of DP(n).reverse()){if(!CP.test(r))continue;let o=r.endsWith("?")&&r.length<=PP&&NP.test(r);if(MP.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function bx(e){let t=String(e||""),n=[];BP.test(t)&&n.push("commit"),yx.test(t)&&n.push("rebase"),jP.test(t)&&n.push("push");let r=FP.test(t);!r&&LP.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 kx(e){return yx.test(String(e||""))}function Os(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)`:"",h=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${h}${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${h}${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 xx(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 vx(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<=UP&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Sx(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+HP||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Tx(e){let t=(Array.isArray(e)?e:[]).filter(r=>fx[r]).map(r=>fx[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 px,mx,hx,wx,CP,MP,NP,PP,Ar,BP,yx,jP,LP,FP,UP,HP,fx,bo=j(()=>{px=/(?<!\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,mx=/(?<!\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,hx=/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,wx=/(?<!\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,CP=new RegExp(`\\b(?:${px}|${mx}|${hx}|${wx})\\b`,"i"),MP=/\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,NP=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,PP=90;Ar=["commit","rebase","push","pr"],BP=new RegExp(`\\b(?:${px})\\b`,"i"),yx=new RegExp(`\\b(?:${wx})\\b`,"i"),jP=new RegExp(`\\b(?:${mx})\\b`,"i"),LP=new RegExp(`\\b(?:${hx})\\b`,"i"),FP=/\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;UP=3e4;HP=2500;fx={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import WP from"node:crypto";import ql from"node:fs/promises";import Ex from"node:path";import qP from"node:os";async function He(){return _r||(qp??=ql.readFile(Gp,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(_r=e,!t&&Ne()){let{records:n,source:r}=await et("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;_r[oe(i.repo,i.number)]=i}Object.keys(_r).length&&await Vp().catch(()=>{})}}return _r}).finally(()=>qp=null),qp)}async function Vp(){let e=JSON.stringify({links:_r},null,2),t=`${Gp}.${process.pid}.tmp`;await ql.mkdir(Ax,{recursive:!0}),await ql.writeFile(t,e),await ql.rename(t,Gp)}async function Qe(e){Kp=Kp.then(Vp,Vp),await Kp.catch(()=>{});let t=_r?.[e];t&&wt("prlinks",{id:_x(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Ot({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 He(),c=oe(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:KP.has(a),firstSeen:Date.now()},await Qe(oe(e,t)),{link:i[c],created:!0})}async function Ln(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||n.announced?null:(n.announced=!0,await Qe(oe(e,t)),n)}async function tt(e,t){return!e||t==null?null:(await He())[oe(e,t)]??null}async function Ir(e,t,n){if(!e||t==null)return null;let r=(await He())[oe(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,delete r.followupPending,await Qe(oe(e,t)),r):null}async function Ix(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[oe(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Qe(oe(e,t)),o):null}async function $x(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[oe(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Qe(oe(e,t)),o):null}async function Kl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Qe(oe(e,t)),r)}async function Gl(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Qe(oe(e,t)),n)}async function Vl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Qe(oe(e,t)),r)}async function zl(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Qe(oe(e,t)),n)}async function Rx(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(r.baseFoldAt=n,await Qe(oe(e,t)),r):null}async function Ox(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Qe(oe(e,t)),n)}async function Cx(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[oe(e,t)];return r?(r.lastHeadSha=n,await Qe(oe(e,t)),r):null}async function Mx(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await He())[oe(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Qe(oe(e,t))),{prev:o,link:r}}async function mi(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Qe(oe(e,t)),r):null}async function ko(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Qe(oe(e,t)),r):null}async function hi(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Qe(oe(e,t)),r):null}async function Nx(e,t,n){if(!e||t==null)return null;let r=(await He())[oe(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Qe(oe(e,t)),r}async function Px(e,t,n){if(!e||t==null)return null;let r=(await He())[oe(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).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.followupPending=[...o.values()],await Qe(oe(e,t)),r}async function wi(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Qe(oe(e,t)),n)}async function Dx(){return Object.values(await He()).filter(e=>!GP.has(e.state))}async function xo(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function Bx(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await He(),r=0;for(let[o,s]of Object.entries(n))await wt("prlinks",{id:_x(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Ax,Gp,oe,_x,_r,qp,Kp,KP,GP,Fn=j(()=>{ur();Ax=process.env.BOSTRAT_STATE_DIR||Ex.join(process.env.HOME||qP.tmpdir(),".bostrat"),Gp=Ex.join(Ax,"pr-links.json"),oe=(e,t)=>`${e}#${Number(t)}`,_x=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=WP.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},_r=null,qp=null,Kp=Promise.resolve();KP=new Set(["api","observed","reconciled"]);GP=new Set(["merged","closed","lost"])});import VP from"node:fs";import zp from"node:fs/promises";import Ux from"node:path";import zP from"node:os";function qx(e,t=!1){if(!t&&e-Lx<YP)return;Lx=e;let n=JSON.stringify({windowStart:Cs,byCaller:Object.fromEntries(Un),lastHour:$r});Fx=Fx.then(()=>zp.mkdir(Hx,{recursive:!0})).then(()=>zp.writeFile(`${Yl}.tmp`,n)).then(()=>zp.rename(`${Yl}.tmp`,Yl)).catch(()=>{})}function Kx(e){e-Cs<Wx||($r={start:Cs,total:Jl(Un),byCaller:Object.fromEntries(Un)},$r.total&&console.log(`[github] ${$r.total} calls last hour`,$r.byCaller),Cs=e,Un=new Map,qx(e,!0))}function Hn(e="other",t=Date.now()){Kx(t),Un.set(e,(Un.get(e)||0)+1),qx(t)}function Gx(e=Date.now()){return Kx(e),{windowStart:Cs,total:Jl(Un),byCaller:Object.fromEntries(Un),lastHour:$r,backoffUntil:vo>e?vo:null}}function Rr(e){return JP.test(String(e?.message||e||""))}function Or(e,t=Date.now()){return vo<=t&&console.warn(`[github] rate limited \u2014 backing off ${jx/6e4} min:`,String(e?.message||e||"").slice(0,200)),vo=Math.max(vo,t+jx),vo}function Xl(e=Date.now()){return Math.max(0,vo-e)}var Hx,Yl,Wx,jx,YP,Cs,Un,$r,vo,Lx,Jl,Fx,JP,gi=j(()=>{Hx=process.env.BOSTRAT_STATE_DIR||Ux.join(process.env.HOME||zP.tmpdir(),".bostrat"),Yl=Ux.join(Hx,"github-metrics.json"),Wx=60*6e4,jx=5*6e4,YP=6e4,Cs=Date.now(),Un=new Map,$r=null,vo=0,Lx=0,Jl=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(VP.readFileSync(Yl,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Wx?(Cs=e.windowStart,Un=new Map(Object.entries(e.byCaller||{})),$r=e.lastHour||null):Jl(new Map(Object.entries(e.byCaller||{})))&&($r={start:e.windowStart,total:Jl(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Fx=Promise.resolve();JP=/rate limit|too quickly|abuse detection|HTTP 429/i});import Vx from"node:fs/promises";import zx from"node:path";import XP from"node:os";function Ql({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};Zl.push(l),Zl.length>QP&&Zl.shift();let u=Yp.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)),Yp.set(e,u),eD(l),l}async function eD(e){try{await Vx.mkdir(Yx,{recursive:!0}),await Vx.appendFile(ZP,JSON.stringify(e)+`
|
|
179
|
-
`)}catch{}}function Jx(){let e={};for(let[t,n]of Yp)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:Zl.slice(-20)}}var Yx,ZP,QP,Zl,Yp,eu=j(()=>{Yx=process.env.BOSTRAT_STATE_DIR||zx.join(process.env.HOME||XP.tmpdir(),".bostrat"),ZP=zx.join(Yx,"github-latency.ndjson"),QP=100,Zl=[],Yp=new Map});import{execFile as tD}from"node:child_process";import{promisify as nD}from"node:util";async function rD(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ns("git",["-C",e,"rev-parse","HEAD"]),Ns("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 Ut(e,t,n={}){let{caller:r="prs",...o}=n,s=await ln({cwd:e});Hn(r);try{let{stdout:a}=await Ns("gh",t,{cwd:e,...oD,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Rr(a)&&Or(a),a}}async function Zp(){if(tu&&Date.now()-tu.at<6e4)return tu.data;let e;try{Hn("auth");let t=await Ns("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 _a();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return tu={at:Date.now(),data:e},e}async function sD(e){try{let{stdout:t}=await Ns("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Zx(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 Jp(e){let t=(e||[]).map(Zx),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 em(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function ev(e,t){if(!t)return null;let n=await Se(e);if(!n)return null;try{let r=await Ut(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 tv(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 nv(e,t){let n=await ev(e,t);return n===null?{...Jp([]),unavailable:!0}:Jp(tv(n))}function nu(e,t=Jp(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 iD(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Ms.delete(s);continue}r.add(s),Ms.has(s)||Ms.set(s,n),o.mergeabilityPending=n-Ms.get(s)<aD}for(let o of Ms.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Ms.delete(o);return t}async function Ps(e,t){let n=await Wn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Ut(n.path,["pr","view",String(r),"--json",Qp]);return nu(JSON.parse(o))}catch(o){if(!em(o))throw o;let s=await Ut(n.path,["pr","view",String(r),"--json",Qx]),a=JSON.parse(s);return nu(a,await nv(n.path,a.headRefOid))}}async function hn(e,{caller:t="prs"}={}){try{let n=await Ut(e,["pr","list","--json",Qp,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>nu(r)).sort(Xx)}catch(n){if(!em(n))throw n;let r=await Ut(e,["pr","list","--json",Qx,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>nu(o,await nv(e,o.headRefOid))))).sort(Xx)}}function gn(){yi.clear()}async function uD(e,t){if(!e)return t;for(let n of t){let r=await tt(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 Kl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Vl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function rv(){let e=await Zp();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await On())await sD(a.path)&&t.push(a);let n=Date.now(),r=Xl(n)>0,o=await Promise.all(t.map(async a=>{let i=yi.get(a.path),c=i?.pending?lD:cD;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Se(a.path).catch(()=>null),u=iD(a.path,await hn(a.path,{caller:"tab"}),n);if(await uD(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Ql({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 yi.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:wn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of yi.keys())s.has(a)||yi.delete(a);return{auth:e,repos:o}}async function Wn(e){let t=(await On()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function So(e){return Zo(e)}async function Ds(e,t,{method:n="squash"}={}){let r=await Wn(e);if(!bi.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 ln({cwd:r.path})).GH_TOKEN){let a=await Hd().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ut(r.path,s)}catch(a){throw new Error(wn(a))}finally{gn()}return{ok:!0,number:o}}async function ki(e,t){let n=await Wn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ut(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(wn(s))}finally{gn()}let o=await Se(n.path).catch(()=>null);return o&&await Rx(o,r).catch(()=>{}),{ok:!0,number:r}}async function tm(e,t){let n=await Wn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ut(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(wn(o))}finally{gn()}return{ok:!0,number:r}}function nm(e){return e.green&&!e.isDraft}function mD(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 ov(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:nm(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function sv(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function xi(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function av(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(xi))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function iv(){let e={};for(let[t,n]of Xp)e[t]={...n};return e}async function cv(e,{method:t="merge",onMerged:n=null}={}){let r=await Wn(e);if(!bi.has(t))throw new Error(`invalid merge method: ${t}`);if(Xp.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await hn(r.path)).filter(nm).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 Xp.set(r.path,s),wD(s,o,{listPrs:()=>hn(r.path),merge:a=>Ds(r.path,a,{method:t}),sleep:pD,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function hD(e,t){let n=Date.now()+(t.settleMaxMs??fD),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(dD)}}async function wD(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 hD(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: ${wn(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(!nm(a)){e.skipped.push({number:a.number,title:a.title,reason:mD(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:wn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),gn()}}async function rm(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ns("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${wn(l)}`)}await rD(e);let o=n||"";if((await ln({cwd:e})).GH_TOKEN){let l=await Hd().catch(()=>null);l&&(o=`${o?`${o}
|
|
180
|
-
|
|
181
|
-
`:""}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
|
|
150
|
+
`});function TN(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 EN(e,t,n){let r=Fk(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 kr(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,m)=>{u.set(y,m),t.push(m)},d=y=>{let m=u.get(y);m&&(m.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,h=new Map;for(let y of e||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let m=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let k of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let O=k[0].split("/").pop().toLowerCase();h.has(O)||h.set(O,[]),h.get(O).push(y.payload?.ts??y.ts)}}let w=3*6e4,g=20*6e4,b=(y,m)=>!!(y?.name&&h.get(String(y.name).toLowerCase()))?.some(O=>O>=m-w&&O<=m+g);for(let y of e||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let m=Ve(y.payload?.text);m&&(o.has(y.boxId)||o.set(y.boxId,[]),o.get(y.boxId).push(m))}else if(y.type==="turn.user"&&y.source==="orchestrator"){let m=Ve(y.payload?.text);m&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(m))}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 m=y.payload.note,k={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:me(y)};m.toolUseId&&i.set(m.toolUseId,k);let O=String(m.summary||"").match(p);O&&l.set(O[1],k)}let v=new Map,x=new Map,I=null,P=new Map,B=new Map,j=new Map,L=new Map,A=new Map,E=new Map,q=new Map,z=new Map,F=new Map,R=new Map,G=new Map,X=new Map,K=new Map,he=new Map,ce=new Map,re=[],Ae=y=>re.push(y),U=y=>{if(!re.length)return;let m=me(y);for(let k of re){if(k.status!=="open"||k.box&&k.box!==y.boxId||(k.ts??0)>m)continue;let O=t.indexOf(k);O>=0&&t.splice(O,1),k.ts=m,t.push(k)}},de=new Map,ae=new Map,Y=new Map,Re=new Map,pe=new Map,Ie=new Map,_=()=>{for(let y of Ie.values())y.questions.length&&t.push(y);Ie.clear()},N=null,xe=6e4,Ne=new Map,Xe=(y,m)=>{let k=o.get(y);if(!k?.length)return!1;let O=Ve(m),le=s.get(y);return k.some(we=>{if(!O.startsWith(we))return!1;let Fe=Ve(O.slice(we.length));return!!Fe&&!!le?.has(Fe)})};for(let y of e||[]){let m=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||(re.length=0,!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length))break;{let k=m.note||Ts(m.text);if(k){if(k.kind==="task"){if(k.toolUseId&&a.has(k.toolUseId))break;let O=String(k.summary||"").match(p);if(!k.toolUseId&&O&&c.has(O[1]))break}if(k.kind==="command"&&Mk(k.command))break;if(k.kind==="resume-summary"){t.push({id:y.id,kind:"resume-summary",title:k.title||"Context resumed from summary",bullets:k.bullets||[],sections:k.sections||[],rawChars:k.rawChars??null,ts:me(y),box:y.boxId,agent:y.provider});break}k.kind!=="suppress"&&t.push({id:y.id,kind:"event",type:Pk(k),name:k.name??null,command:k.command??null,summary:k.summary??null,status:k.status??null,ts:me(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${me(y)}`)||y.source==="transcript"&&Xe(y.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){t.push({id:y.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:me(y),box:y.boxId,agent:y.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"){m.kind==="prompt-answer"&&(N=null);break}for(let k of P.values())k.status="stale";P.clear();for(let k of j.values())k.status="stale";j.clear();for(let k of K.values())k.status="stale";K.clear(),N&&N.box===y.boxId&&(t.push(TN(N)),N=null),t.push({id:y.id,kind:"message",role:"user",text:m.text,ts:me(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...y.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let k=m.from||{};t.push({id:y.id,kind:"event",type:"peer-message",text:m.text,from:k.trackName||k.boxId||k.name||null,fromTrackId:k.trackId||null,ts:me(y),box:y.boxId,agent:y.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let k=Vb(m,{codexFailedText:co});if(k){t.push({id:y.id,kind:"event",type:"provider-error",cls:k.cls,status:k.status??void 0,text:k.text,provider:y.provider,legacy:!0,at:me(y),ts:me(y),box:y.boxId,agent:y.provider});break}_();let O=(m.images||[]).filter(Me=>!b(Me,me(y)));if(!String(m.text||"").trim()&&!O.length)break;if(y.provider==="cursor"){let Me=`${y.boxId}|${m.text}`;if(n.has(Me))break;n.add(Me)}(!N||N.box===y.boxId)&&(N=null);let le=y.provider==="codex"?(m.tools||[]).filter(Me=>!Kf.has(Me)&&!Za.has(Me)):m.tools||[],we=m.text,Fe=null,Lt=pp(String(m.text||""));Lt&&(we=Lt.text,Fe={questions:Lt.questions}),t.push({id:y.id,kind:"message",role:"assistant",text:we,ts:me(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...Fe?{ask:Fe}:{},...le.length?{tools:le}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...O.length?{images:O}:{}}),U(y),Fe&&(N={id:y.id,groupId:mp(y.provider,m.sourceId||y.id,Fe.questions),provider:y.provider,box:y.boxId,questions:Fe.questions,ts:me(y)});break}case"job.started":{if(!m.id)break;let k=me(y),O=i.get(m.id);if(!O&&(m.kind||"agent")==="agent"&&m.name){let Me=l.get(m.name);Me&&Me.ts>=k&&(O=Me)}let le={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:O?O.status==="killed"?"stopped":O.status&&O.status!=="completed"?"failed":"done":"running",startedTs:k,finishedTs:O?.ts??null,summary:O?.summary??null,resultHead:O?.resultHead??null},we=Ne.get(y.boxId),Fe=we?.jobs[we.jobs.length-1]?.startedTs;if(we&&Fe!=null&&k-Fe<=xe)we.jobs.push(le);else{let Me={id:y.id,kind:"job-dock",dockId:m.id,ts:k,box:y.boxId,agent:y.provider,jobs:[le]};Ne.set(y.boxId,Me),t.push(Me)}break}case"handoff.done":t.push(ft(y,"lead-handoff"));break;case"subtask.delegated":t.push({...ft(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":t.push(ft(y,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let O of P.values())O.status="stale";P.clear();for(let O of j.values())O.status="stale";j.clear()}let k=ft(y,`pr-${m.change}`);if(k.box=y.boxId??null,AN.has(m.change)&&m.number!=null){let O=EN(v,x,m);if(v.size>=2){for(let we of x.values())for(let Fe of we)Fe.grouped=!0;k.groupPrs=[...v.values()];let le=[k];for(let we of v.keys())x.set(we,le)}else{let le=x.get(O)??[];le.push(k),x.set(O,le)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let O=Fk(m),le=I&&!I.complete||v.size>=2;if(v.has(O))v.delete(O),x.delete(O);else if(m.number!=null)for(let we of[...v.keys()])we.endsWith(`#${m.number}`)&&(v.delete(we),x.delete(we));if(le&&m.number!=null){(!I||I.complete)&&(I={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:me(y),ts:me(y),box:y.boxId,agent:y.provider},t.push(I)),I.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:me(y)}),I.remaining=[...v.values()],I.complete=v.size===0;break}}t.push(k);break}case"pr.offered":{if(!m.offerId)break;for(let O of P.values())O.status="stale";P.clear();for(let O of j.values())O.status="stale";j.clear();let k={id:y.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:y.source==="cli"||m.via==="cli"?"cli":"transcript",ts:me(y),box:y.boxId,agent:y.provider};P.set(m.offerId,k),B.set(m.offerId,k),t.push(k),k.source==="cli"&&Ae(k);break}case"pr.offer.answered":{let k=B.get(m.offerId);k&&(k.status=m.action||"dismiss",k.actions=Array.isArray(m.actions)?m.actions:null,k.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(k.existingPr={number:m.existingPr.number??null}),P.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let O of j.values())O.status="stale";j.clear();let k={id:y.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:me(y),box:y.boxId,agent:y.provider};j.set(m.offerId,k),L.set(m.offerId,k),t.push(k);break}case"pr.exists.answered":{let k=L.get(m.offerId);k&&(k.status=m.choice||"dismiss",j.delete(m.offerId));break}case"box.state.changed":{let k=m.change==="model"?"model-change":m.change;if(!k)break;let O=ft(y,k);if(k==="model-change"&&(O.to=m.model),k==="node-update"&&O.latest){let le=t.findIndex(we=>we.kind==="event"&&we.type==="node-update"&&we.latest===O.latest);if(le>=0){let we=t[le];t[le]={...O,ts:we.ts,at:we.at,firstAt:we.firstAt??we.ts,updatedAt:O.ts};break}}t.push(O);break}case"track.transferred":case"track.recovered":t.push(ft(y,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){d(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;f(`view:${m.viewId}`,{id:y.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:me(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{t.push({id:y.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:me(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(m.change==="picked"){t.push({id:y.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:me(y),box:y.boxId,agent:y.provider});break}if(m.change==="deleted"){d(`mock:${m.mockId}`);break}if(m.change!=="created")break;f(`mock:${m.mockId}`,{id:y.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:me(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(m.change==="deleted"){d(`flow:${m.flowId}`);break}if(m.change!=="created")break;f(`flow:${m.flowId}`,{id:y.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:me(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){d(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;f(`runbook:${m.runbookId}`,{id:y.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:me(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{t.push({id:y.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:me(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let O of q.values())O.box===(m.box||y.boxId)&&(O.status="stale",q.delete(O.offerId));let k={id:y.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:me(y),box:m.box||y.boxId,agent:m.agent||y.provider};q.set(m.offerId,k),z.set(m.offerId,k),t.push(k);break}case"box.fullscreen.answered":{let k=z.get(m.offerId);k&&(k.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",q.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let O of A.values())O.key===m.key&&(O.status="stale",A.delete(O.offerId));let k={id:y.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:me(y),box:y.boxId,agent:y.provider};A.set(m.offerId,k),E.set(m.offerId,k),t.push(k);break}case"ticket.move.answered":{let k=E.get(m.offerId);k&&(k.status=m.action==="moved"?"moved":"dismiss",A.delete(m.offerId));break}case"ticket.state.changed":{for(let k of A.values())k.key===m.key&&k.to===m.to&&(k.status="stale",A.delete(k.offerId));if(m.mode==="offer")break;t.push(ft(y,"ticket-state"));break}case"ticket.commented":{t.push({id:y.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:me(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":t.push(ft(y,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let O of F.values())O.status="stale";F.clear();let k={id:y.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:me(y),box:y.boxId,agent:y.provider};F.set(m.proposalId,k),R.set(m.proposalId,k),t.push(k),Ae(k);break}case"ticket.create.answered":{let k=R.get(m.proposalId);k&&(k.status=m.action==="created"?"created":"dismiss",k.createdCount=m.createdCount??null,k.failedCount=m.failedCount??null,F.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let O of G.values())O.status="stale";G.clear();let k={id:y.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:me(y),box:y.boxId,agent:y.provider};G.set(m.proposalId,k),X.set(m.proposalId,k),t.push(k),Ae(k);break}case"loop.create.answered":{let k=X.get(m.proposalId);k&&(k.status=m.action==="created"?"created":"dismiss",G.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let k=m.replaces?ce.get(m.replaces):null;k&&k.status==="armed"&&(k.status="replaced");let O={id:y.id,kind:"followup-armed",armId:m.armId,trigger:m.trigger??"now",label:m.label??"",prompt:m.prompt??"",why:m.why??null,status:"armed",ts:me(y),box:y.boxId,agent:y.provider};ce.set(m.armId,O),O.trigger!=="now"&&t.push(O);break}case"followup.armed.cancelled":{let k=ce.get(m.armId);k&&k.status==="armed"&&(k.status="cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let le of K.values())le.trigger===m.trigger&&(le.status="stale",K.delete(le.offerId));let k=Array.isArray(m.rows)?m.rows:[];for(let le of k){let we=le.armId?ce.get(le.armId):null;we&&we.status==="armed"&&(we.status="offered")}let O={id:y.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},rows:k.map(le=>({followupId:le.followupId??null,armId:le.armId??null,label:le.label??"",prompt:le.prompt??null,promptPreview:le.promptPreview??null,promptHash:le.promptHash??null,origin:le.origin??"operator",why:le.why??null})),status:"open",answer:null,ts:me(y),box:y.boxId,agent:y.provider};K.set(m.offerId,O),he.set(m.offerId,O),t.push(O),O.trigger==="now"&&Ae(O);break}case"followup.answered":{let k=he.get(m.offerId);k&&(k.status=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss",k.answer={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0},K.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let O of de.values())O.status="stale";de.clear();let k={id:y.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:me(y),box:y.boxId,agent:y.provider};de.set(m.proposalId,k),ae.set(m.proposalId,k),t.push(k),Ae(k);break}case"followup.create.answered":{let k=ae.get(m.proposalId);k&&(k.status=m.action==="created"?"created":"dismiss",de.delete(m.proposalId));break}case"followup.created":{let k=m.proposalId?ae.get(m.proposalId):null;k&&(k.created={followupId:m.followupId??null});break}case"loop.created":{let k=m.proposalId?X.get(m.proposalId):null;k&&(k.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(ft(y,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let le of Y.values())le.loopId===m.loopId&&(le.status="stale",le.staleReason="superseded",Y.delete(le.proposalId));let k=y.type==="loop.update.offered",O={id:y.id,kind:k?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...k?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:me(y),box:y.boxId,agent:y.provider};Y.set(m.proposalId,O),Re.set(m.proposalId,O),t.push(O),Ae(O);break}case"loop.update.answered":{let k=Re.get(m.proposalId);k&&(m.action==="moved"?(k.status="stale",k.staleReason="moved"):k.status=m.action==="applied"?"applied":"dismiss",Y.delete(m.proposalId));break}case"loop.delete.answered":{let k=Re.get(m.proposalId);k&&(m.action==="moved"?(k.status="stale",k.staleReason="moved"):k.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",Y.delete(m.proposalId));break}case"loop.updated":{let k=m.proposalId?Re.get(m.proposalId):null;k&&(k.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(ft(y,"loop-updated"));break}case"loop.deleted":t.push(ft(y,"loop-deleted"));break;case"loop.paused":t.push(ft(y,"loop-paused"));break;case"rework.cause.offered":{if(!m.sha||pe.has(m.sha))break;let k={id:y.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,intent:m.intent??null,shas:Array.isArray(m.shas)?m.shas:null,pr:m.pr??null,retracts:0,status:"open",cause:null,ts:me(y),box:y.boxId,agent:y.provider};pe.set(m.sha,k),t.push(k);break}case"rework.cause.answered":{let k=m.sha?pe.get(m.sha):null;k&&(k.status="answered",k.cause=m.cause??null);break}case"rework.cause.retracted":{let k=m.sha?pe.get(m.sha):null;k&&(k.status="open",k.cause=null,k.retracts=(k.retracts??0)+1);break}case"ticket.created":{let k={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},O=m.proposalId?R.get(m.proposalId):null;if(O){O.created=k;break}t.push({id:y.id,kind:"ticket-created",...k,ts:me(y),box:y.boxId,agent:y.provider});break}case"error":{if(!m.kind)break;t.push(ft(y,m.kind));break}case"provider.error":t.push({...ft(y,"provider-error"),provider:y.provider,box:y.boxId,agent:y.provider});break;case"usage.cap.hit":t.push(ft(y,"usage-cap-hit"));break;case"usage.cap.warning":t.push(ft(y,"usage-cap-warning"));break;case"provider.connected":{t.push({id:y.id,kind:"event",type:"provider-connected",provider:m.provider||y.provider,ts:me(y),at:me(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(N=null,m.control||!m.question)break;let k=m.groupId||m.promptId;if(!k)break;let O=Ie.get(k);O||(O={id:y.id,kind:"prompt",groupId:k,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:m.questionTotal??1,questions:[],ts:me(y)},Ie.set(k,O));let le=m.questionIndex??0,we={index:le,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},Fe=O.questions.findIndex(Me=>Me.index===le&&Ve(Me.question)===Ve(we.question));Fe>=0?O.questions[Fe]=we:O.questions.push(we),O.questions.sort((Me,Jt)=>Me.index-Jt.index),O.questionTotal=Math.max(O.questionTotal,m.questionTotal??1),O.questions.length>=O.questionTotal&&(t.push(O),Ie.delete(k));break}default:break}}let wt=u.size?t.filter(y=>!y.retired):t;return IN(wt.sort((y,m)=>(y.ts??0)-(m.ts??0)))}function IN(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<=_N){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 gp(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:$N(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??kr(e)).filter(o=>o.kind==="message");return Lb(n,r)}function $N(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ys(e.command)?t:yr(e.command,e.description||"")}function ft(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:me(e),ts:me(e)}}function Uk(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)+RN,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&ON.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Hk(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!MN.has(r.type)||!r.box)continue;let o=(r.ts??0)+CN,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 Wk(e){return(e||[]).filter(t=>NN.has(t?.kind)&&t.status==="open")}var me,AN,Fk,_N,RN,ON,CN,MN,NN,yp=H(()=>{bs();lo();yl();El();Cl();wp();me=e=>e.payload?.ts??e.ts;AN=new Set(["opened","ci-fixed","reconciled","pushed"]),Fk=e=>`${e.repo??""}#${e.number}`;_N=2*6e4;RN=12e4,ON=new Set(["archived","unarchived","opened"]);CN=2500,MN=new Set(["pr-opened","pr-merged","pr-auto-armed"]);NN=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","fullscreen-offer","followup-create-offer"])});function LN(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 UN(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 HN(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<BN&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<jN&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Vk(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=LN(e),s=UN(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=kr(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-PN);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Kk[r]??Kk.failover),l.push(""),o.lastPush?.sha){let d=Gk(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=Gk(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=HN(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"}:** ${FN(d.text,DN)}`),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(`
|
|
151
|
+
`);return f.length<=qk?f:`${f.slice(0,qk)}
|
|
152
|
+
|
|
153
|
+
[\u2026 brief truncated]`}var PN,DN,qk,BN,jN,FN,Gk,Kk,zk=H(()=>{yp();PN=12,DN=2e3,qk=48e3,BN=30,jN=10;FN=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},Gk=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Kk={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.",get recover(){return this.failover},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 WN from"node:fs";import xr from"node:fs/promises";import vr from"node:path";function n0(e){let t=oo(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=Yk?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,Yk).toString("utf8").replace(/�+$/,"")}
|
|
154
|
+
|
|
155
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function ZN(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Xk.test(s)){if(r.length>=c0)continue;let a=XN(s.replace(Xk,"").replace(/[.;,:]+$/,""),l0,YN);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>Jk&&(o=`${o.slice(0,Jk-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function u0(e,t,{timeoutMs:n=9e4}={}){let r=vr.join(t,VN),o=vr.join(r,zN);await xr.mkdir(r,{recursive:!0});let s=await Ml(o),a=await ke(e);if(!a)throw new Error(`session "${e}" not found`);return await Nl(e,a,JN(o)),await g0(o,s,n),ZN(await xr.readFile(o,"utf8"))}function d0(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===o0||n===i0;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(r0)||n===qN||r.startsWith(s0)||r.startsWith(a0)}function f0(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-QN;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 p0(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 m0(e,{windows:t=[]}={}){if(d0(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 Pl(e){return bp.get(e)||null}async function h0(e){let t=bp.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ke(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:vr.join(n.cwd,e0,t0)};return bp.set(e,r),nP(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function nP(e,t){e.step="summarize",await w0(e.name,t),e.step="clear";let n=await ke(e.name);await Nl(e.name,n,"/clear"),await vp(eP),e.step="resume",await Nl(e.name,n,KN),e.step="done",e.finishedAt=Date.now()}async function w0(e,t,{timeoutMs:n=tP}={}){let r=vr.join(t,e0),o=vr.join(r,t0);await xr.mkdir(r,{recursive:!0});let s=await Ml(o),a=await ke(e);await ue({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Nl(e,a,GN(o)),await g0(o,s,n),await ue({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Ml(o)});let i=await Ek(t,o,{exclude:u=>d0(u.role,u.text)});await ue({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 xr.readFile(o,"utf8");await $({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...n0(u),ts:Date.now()}})}catch{}}await kf(t);let l=await rP(r,o);return c&&await ib(c,l||o,{box:e,agent:a?.agent??null}),o}async function kp(e,{timeoutMs:t}={}){let n=await ke(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return w0(e,n.cwd,t?{timeoutMs:t}:void 0)}async function xp(e,t,{trackName:n="this Track"}={}){let r=await W({id:e,cwd:t}),o=Vk(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=vr.join(t,Zk);return await xr.mkdir(vr.dirname(s),{recursive:!0}),await xr.writeFile(s,o,"utf8"),await $({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...n0(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Zk}async function Ml(e){try{return(await xr.stat(e)).mtimeMs}catch{return 0}}async function g0(e,t,n){let r=Date.now()+n;for(;;){if(await Ml(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await vp(2e3)}}async function rP(e,t){try{if(!WN.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=vr.join(e,`${n}.md`);return await xr.copyFile(t,r),r}catch{return null}}function vp(e){return new Promise(t=>setTimeout(t,e))}function Qk(e){return e==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":""}async function Nl(e,t,n){if(t){let o=Qk(t.agent);await br({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ke(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await He(e,["Escape"],{kind:"drive-wipe"}),await vp(150),await ms(e,hl,{kind:"drive-wipe"}),await tt(e,n,{kind:"orchestration-drive",...gn(r.agent,n)}),await ai({sessionName:e,agent:r.agent,agentCmd:Qk(r.agent),text:n,flow:"orchestration-drive"})}var e0,t0,Yk,r0,o0,s0,qN,GN,KN,a0,i0,VN,zN,Jk,c0,l0,YN,JN,Xk,XN,QN,eP,tP,bp,Zk,Sp=H(()=>{ut();ni();hn();ri();ip();_k();Pa();ge();al();zk();e0=".bostrat/handoff",t0="latest.md",Yk=32*1024;r0="Checkpoint: write a work-in-progress handoff",o0="HANDOFF WRITTEN",s0="Resume from .bostrat/handoff/latest.md",qN="/clear",GN=e=>`${r0} 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 ${o0} and take no further action.`,KN=`${s0}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,a0="Beat: write a",i0="BEAT WRITTEN",VN=".bostrat/arc",zN="beat.txt",Jk=200,c0=4,l0=5,YN=36,JN=e=>`${a0} 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 ${c0} further lines, each starting with "- " and naming ONE piece in at most ${l0} 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 ${i0} and take no further action.`,Xk=/^(?:[-–—*•·]|\d+[.)])\s+/,XN=(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};QN=6e5;eP=3500,tP=3e5,bp=new Map;Zk=".bostrat/handoff/status-latest.md"});function Tr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Dl.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 k0(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:Tr(o.from,"from",n),toMs:Tr(o.to,"to",n)}))}return t}function Ap(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 sP(e){if(!e)return null;let t=String(e).trim();return Sr[t]?t:y0[t]?y0[t]:Object.keys(Sr).find(r=>t.startsWith(r))||null}function aP(e){if(!e||e===Sr)return oP;let t=b0.get(e);return t||(t=k0(e),b0.set(e,t)),t}function x0(e,t,n={}){let{table:r,speed:o}=n,s=aP(r),a=r&&r!==Sr?s[e]?e:null:sP(e);if(!a)return null;let i=Ap(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function yn(e,t={},n,r){let o=x0(e,n,r);return o?((t.input||0)*o.input+(t.output||0)*o.output+(t.cacheWrite||0)*o.cacheWrite+(t.cacheRead||0)*o.cacheRead)/1e6:0}function iP(e=Sr){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:Tr(a.from,"from",n),toMs:Tr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),Dl.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`),a.fast&&(typeof a.fast[i]!="number"||!Number.isFinite(a.fast[i])||a.fast[i]<0)&&t.push(`${n}: fast variant ${i} must be a non-negative number, got ${JSON.stringify(a.fast?.[i])}`)}}return t}function lP({now:e=Date.now(),table:t=Sr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...cP,...n},a=[],i=Ap(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(f=>({...f,fromMs:Tr(f.from,"from",c),toMs:Tr(f.to,"to",c),verifiedMs:Dl.test(String(f.verifiedAt??""))?Date.parse(`${f.verifiedAt}T00:00:00.000Z`):NaN}))}catch(f){a.push(String(f.message));continue}for(let f of u){if(!Number.isFinite(f.verifiedMs)){a.push(`${c}: window ${f.from??"\u2026"} \u2192 ${f.to??"now"} has no verifiedAt`);continue}let d=i>=f.fromMs&&i<=f.toMs,p=f.fromMs>i&&f.fromMs-i<=o*Tp;d&&i-f.verifiedMs>r*Tp&&a.push(`${c}: current rate last verified ${f.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),p&&f.fromMs-f.verifiedMs>s*Tp&&a.push(`${c}: scheduled rate change on ${f.from} was last verified ${f.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function As({now:e=Date.now(),table:t=Sr,...n}={}){let r=Ap(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=Tr(l.from,"from",i),f=Tr(l.to,"to",i);r>=u&&r<=f&&Dl.test(String(l.verifiedAt??""))&&(!o||l.verifiedAt<o)&&(o=l.verifiedAt),u>r&&s.push({model:i,from:l.from,verifiedAt:l.verifiedAt??null})}s.sort((i,c)=>i.from<c.from?-1:1);let a=[...iP(t),...lP({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Sr,y0,Dl,oP,b0,v0,cP,Tp,Fn=H(()=>{Sr={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},y0={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"},Dl=/^\d{4}-\d{2}-\d{2}$/;oP=k0(Sr);b0=new WeakMap;v0=(e,t,n)=>x0(e,t,n)!==null;cP=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Tp=864e5});function uP(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function T0(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Ut(e){let t=e.payload?.model||"unknown",n=uP(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=yn(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!v0(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function Ar(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=S0();for(let d of n){let{usd:p,model:h,tokens:w,repriced:g,unpriced:b,asRecorded:v}=Ut(d);i+=v;let x=d.provider||"unknown",I=d.boxId||"unattributed";if(g&&c++,b&&(l++,u.add(h)),a+=p,T0(f,w),r[h]||={usd:0,tokens:S0(),events:0},r[h].usd+=p,r[h].events+=1,T0(r[h].tokens,w),o[x]||={usd:0,events:0},o[x].usd+=p,o[x].events+=1,s[I]||={usd:0,events:0,provider:x,models:new Set,label:t[I]||I},s[I].usd+=p,s[I].events+=1,h!=="unknown"&&s[I].models.add(h),d.epoch!=null){let P=(s[I].epochs||={})[d.epoch]||={usd:0,events:0,provider:x,models:new Set};P.usd+=p,P.events+=1,P.provider=x,h!=="unknown"&&P.models.add(h)}}return{totalUsd:Es(a),asRecordedUsd:Es(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Bl(r,d=>({...d,usd:Es(d.usd)})),byProvider:Bl(o,d=>({...d,usd:Es(d.usd)})),byBox:Bl(s,d=>({usd:Es(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Bl(d.epochs,p=>({usd:Es(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var S0,Es,Bl,Er=H(()=>{Fn();S0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});Es=e=>Math.round(e*1e4)/1e4,Bl=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function dP(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 A0(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(Ep(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Ep(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(`${Ep(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Ar(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?dP(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Ep,E0=H(()=>{Er();Ep=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as fP}from"node:child_process";import{promisify as pP}from"node:util";import{randomUUID as mP}from"node:crypto";async function _p(e,t){let n=Date.now()+t;for(;;){if(await Bn(e))return!0;if(Date.now()>n)return!1;await di(wP)}}async function yP(e,t){let n=I0[t]||I0.default;n.escape&&(await He(e,["Escape"],{kind:"swap-exit"}),await di(150)),n.wipe&&await ms(e,hl,{kind:"swap-exit"}),await wl(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=gn(t).submitDelayMs||0;await di(Math.max(r,250)),await He(e,["Enter"],{kind:"swap-exit"})}async function $0(e,t){try{let{stdout:n}=await fo(J,Z("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function R0(e){await fo(J,Z("respawn-pane","-k","-t",e))}async function Ip(e,t={}){if(!Le(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ke(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||hr(n),a=Number(t.exitTimeoutMs)||hP;await ue({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Bn(e)&&(await yP(e,o),!await _p(e,a)&&(i=!0,await ue({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await R0(e),!await _p(e,_0))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&so(n),l=c?Uc():null,u=await $0(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await fo(J,Z("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await fo(J,Z("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?ll(g,l):g}if(f&&!i&&(await R0(e),!await _p(e,_0)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,p=t.resumeSessionId||mP(),h=d?n:Ga(n,p,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await cr(r.cwd),s==="codex"&&await ts(r.cwd),s==="cursor"&&await ns(r.cwd),await ao(e),await fo(J,Z("set-option","-t",e,"@bostrat-sid",p)),await fo(J,Z("set-option","-t",e,"@bostrat-agent",s)),await di(gP),c&&qa().length&&(await $0(e,Wa)||await fo(J,Z("set-environment","-t",e,Wa,"1")).catch(()=>{}),await tt(e,`export ${Wa}=1`,{kind:"swap-env"}),await di(150)),await tt(e,h,{kind:"swap-launch"}),await ue({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:f,launchCmd:h}}var fo,di,hP,_0,wP,gP,I0,O0=H(()=>{ut();Ia();Wc();qc();ri();hn();fo=pP(fP),di=e=>new Promise(t=>setTimeout(t,e)),hP=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,_0=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,wP=250,gP=400,I0={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 C0(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await At(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
156
|
+
`)){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?ys(p):null),detail:p?ys(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&bP.test(kP(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 bP,kP,M0=H(()=>{tn();bs();bP=/^Async (?:agent|task) launched/i,kP=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 _r(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 xP(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 N0(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:nn(`read ${_r(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${_s(r)}`:`Read ${_s(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?_r(t.path,n):null,s=t?.glob?` --glob ${$p(t.glob)}`:"",a=nn(String(r),40);return{command:nn(`grep ${$p(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?_r(t.target_directory,n):null;return{command:nn(`glob ${$p(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${nn(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:nn(`edit ${_r(t.path,n)}`,140),description:`Edit ${_s(t.path)}`}:null;case"Write":return t?.path?{command:nn(`write ${_r(t.path,n)}`,140),description:`Write ${_s(t.path)}`}:null;case"Delete":return t?.path?{command:nn(`delete ${_r(t.path,n)}`,140),description:`Delete ${_s(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>_r(s,n)).filter(Boolean);return{command:nn(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=xP(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:nn(`apply_patch ${_r(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${_s(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:nn(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var jl,nn,_s,$p,P0=H(()=>{jl=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),nn=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,_s=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);$p=e=>`"${String(e).replace(/"/g,'\\"')}"`});import fi from"node:fs";import vP from"node:fs/promises";import po from"node:path";import SP from"node:os";import TP from"node:crypto";import AP from"node:zlib";function j0(e){let t=TP.createHash("md5").update(String(e)).digest("hex");return po.join(EP,t)}function L0(e){try{let t=JSON.parse(fi.readFileSync(po.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 _P(e){let t=j0(e),n;try{n=fi.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=po.join(t,o.name),a=po.join(s,"store.db");if(!fi.existsSync(a))continue;let i=L0(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 Ll(e,t){if(!e)return null;if(t){let n=po.join(j0(e),t),r=po.join(n,"store.db");if(fi.existsSync(r))return{id:t,dir:n,store:r,...L0(n)||{createdAtMs:null,updatedAtMs:null}}}return _P(e)}function D0(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function IP(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=AP.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function $P(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 RP(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!jl.has(r.toolName))continue;let o=N0(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 Rp(e,t){if(!B0)return Is;let{DatabaseSync:n}=B0,r;try{r=new n(e,{readOnly:!0})}catch{return Is}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 Is;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Is}let a=s.lastUsedModel||null,i=s.latestRootBlobId?D0(r,s.latestRootBlobId):null;if(!i)return{...Is,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of $P(i)){let h=IP(D0(r,p));if(!h)continue;let w=h.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),h.role!=="assistant")continue;if(Array.isArray(h.content))for(let v of h.content){let x=v?.providerOptions?.cursor?.modelName;x&&(f=x)}l.push(...RP(h.content,t).map((v,x)=>({...v,id:v.id||`${p}:${x}`}))),u.push(...CP(h.content));let g=OP(h.content);if(!g)continue;let b=[...new Set(u)];u=[],c.push({id:p,text:g,tools:b.length?b:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Is}finally{try{r.close()}catch{}}}function F0(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(fi.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 U0(e,t,{limit:n=80}={}){let r=Ll(e,t);if(!r)return[];let{turns:o,model:s}=await Rp(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=F0(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 H0(e,t){let n=Ll(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Rp(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 W0(e,t,{limit:n=80}={}){let r=Ll(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await Rp(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=F0(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:yr(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:yr(u.command,u.description),ts:i+1});return l}async function q0(e,t){let n=Ll(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await vP.stat(n.store)).mtimeMs)}catch{return null}}var EP,OP,CP,B0,Is,Op=H(()=>{bs();P0();EP=process.env.BOSTRAT_CURSOR_CHATS||po.join(SP.homedir(),".cursor","chats");OP=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(`
|
|
157
|
+
`).trim():"",CP=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],B0=process.getBuiltinModule?.("node:sqlite"),Is={turns:[],trailingCommands:[],model:null,requestIds:[]}});import MP from"node:fs/promises";function Pp(e){return G0.set(e.agent,e),e}function pi(e){return G0.get(e)||NP}async function Bp(e,t={}){return(await pi(e.agent).readMessages(e,t)).map(Dp(e))}async function K0(e,t={}){return(await pi(e.agent).readCommands(e,t)).map(Dp(e))}async function V0(e,t={}){return(await pi(e.agent).readJobs(e,t)).map(Dp(e))}var $s,Cp,Mp,Np,NP,G0,Dp,z0=H(()=>{tn();bl();bs();M0();Op();lo();$s=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}},Cp=class extends $s{transcript(t){return!t?.cwd||!t?.sessionId?null:en(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?zf(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?jb(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?C0(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await MP.stat(n)).mtimeMs)}catch{return null}}},Mp=class extends $s{async readMessages(t,n){return U0(t.cwd,t.sessionId,n)}async readCommands(t,n){return W0(t.cwd,t.sessionId,n)}async updatedAt(t){return q0(t.cwd,t.sessionId)}},Np=class extends $s{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Hb(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Wb(t.cwd,this.sessionId(t),n)}async updatedAt(t){return qb(t.cwd,this.sessionId(t))}},NP=new $s("unknown"),G0=new Map;Pp(new Cp("claude",Wf));Pp(new Mp("cursor",jl));Pp(new Np("codex",Za));Dp=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function wo(e){let t=e.search(Up);return(t>=0?e.slice(0,t):e).trim()}function PP(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function DP(e){let t=String(e).trim();return!!(!t||ho.test(e)||Up.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function BP(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=jP(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function jP(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 J0(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function LP(e,t=""){let n=String(e);if(!Z0.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
158
|
+
`),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(mo);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&&!ho.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:wo(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 UP(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
159
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Q0.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(mo);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&&!(FP.test(n[c])||ho.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:wo(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 HP(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
160
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(mo);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 qP(e,t=""){let n=String(e);if(!jp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
161
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(WP.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(mo);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&&!ho.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>jp.test(u.label)))return null;let i=s.map(u=>({label:wo(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 Hp(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(mo);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 GP(e,t=""){let n=String(e);if(!ex.test(n)||!tx.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=Hp(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:wo(s.label),description:""}))}]}:null}function KP(e,t=""){let n=String(e);if(!nx.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=Hp(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:wo(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 zP(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!VP.test(o)||Wp.test(e[r]))break;n+=o}return n}function YP(e,t=""){let n=String(e);if(!Wp.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
164
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=zP(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 XP(e,t=""){let n=String(e);if(!Lp.test(n)||!JP.test(n)||go.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
165
|
+
`)].reverse().find(a=>Lp.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 ZP(e,t=""){let n=String(e);if(!rx.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o=Hp(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:wo(s.label),description:""}))}]}:null}function QP(e,t=""){let n=String(e);if(go.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
167
|
+
`),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(ox);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(ho.test(r[l])||Up.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 eD(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(Fp.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 tD(e,t=""){let n=String(e);if(!X0.test(n)||!Fp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
|
+
`),o=eD(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let L=i;L<c;L++){let A=r[L].match(mo);A&&(u<0&&(u=L),l.push({line:L,cursor:!!A[1],checked:A[3]!=null&&A[3]!==" ",hasCheckbox:A[3]!=null,label:A[4].trim(),desc:""}))}if(!l.length)return null;for(let L of l)for(let A=L.line+1;A<Math.min(r.length,L.line+4)&&!(mo.test(r[A])||ho.test(r[A]));A++){let E=r[A].trim();if(!(!E||DP(r[A])||Y0.test(E))){L.desc=PP(E);break}}let f=l.filter(L=>!Y0.test(L.label));if(!f.length)return null;let d=f.some(L=>L.hasCheckbox),p=Math.max(0,f.findIndex(L=>L.cursor)),h=s>=0?s+1:0,w=[];for(let L=h;L<u;L++){let A=r[L].trim();A&&!ho.test(r[L])&&!/[☐☒]/.test(r[L])&&w.push(A)}let g=w.join(" ")||(s>=0?J0(r[s]):"")||"The agent is asking a question",b=s>=0?BP(r[s],g):null,v=b?.currentTabLabel||(s>=0?J0(r[s]):""),x=f.map(L=>({label:wo(L.label),description:L.desc})),I=x.map(L=>L.label).join("|"),B=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${v}:${g}:${I}`.slice(0,240),j=b?`claude:ask:${b.tabs.map(L=>L.label).join("|")}`.slice(0,240):B;return{provider:"claude",box:t,multiSelect:d,cursorIndex:p,promptId:B,groupId:j,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:g,header:v,multiSelect:d,options:x}]}}function yo(e,t){let n=String(t||"");return n?e==="claude"?X0.test(n)&&Fp.test(n)||Z0.test(n)||jp.test(n)||Q0.test(n.trimEnd().split(`
|
|
169
|
+
`).pop()||"")?!0:go.test(n)?!1:ex.test(n)&&tx.test(n)||nx.test(n)||Wp.test(n)||Lp.test(n)||rx.test(n)||ox.test(n.trimEnd().split(`
|
|
170
|
+
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function Ir(e,t){return!e||!t?null:e.agent==="claude"?HP(t,e.name)||tD(t,e.name)||LP(t,e.name)||qP(t,e.name)||XP(t,e.name)||YP(t,e.name)||KP(t,e.name)||GP(t,e.name)||ZP(t,e.name)||QP(t,e.name)||UP(t,e.name):e.agent==="cursor"||e.agent==="codex"?sk(e.agent,t,e.name):null}async function qp(e){if(!e)return null;let t=await jn(e.name).catch(()=>"");return Ir(e,t)}function sx(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 ax(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 X0,Fp,mo,Y0,ho,Up,Z0,Q0,FP,jp,WP,go,ex,tx,nx,Wp,VP,Lp,JP,rx,ox,Gp=H(()=>{ut();xl();X0=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Fp=/Chat about this/i,mo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Y0=/^(type something|chat about this|submit)\b/i,ho=/^[─—-]{6,}\s*$/,Up=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;Z0=/^\s*Switch model\?\s*$/m;Q0=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,FP=/^\s*[─—▔-]{6,}\s*$/;jp=/Resume from summary/i,WP=/^\s*This session is\b/;go=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;ex=/Choose the text style that looks best with your terminal/,tx=/Let's get started\./;nx=/Select login method:/;Wp=/Paste code here if prompted/,VP=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;Lp=/OAuth error:/,JP=/Press Enter to retry\./;rx=/Quick safety check: Is this a project you created or one you trust/;ox=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Fl(e,t,n){if(!e||!t)return null;for(let r of nD)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function oD(e){let t=Kp.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},Kp.set(e,t)),t}function lx(e,t,n=Date.now()){let r=oD(e);if(r.sentAt=r.sentAt.filter(s=>n-s<cx),r.sentAt.length>=ix)return{action:"give-up"};if(n-r.last<rD)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 ux(e,t=Date.now()){let n=Kp.get(e);return n?n.sentAt.filter(r=>t-r<cx).length>=ix:!1}var nD,rD,ix,cx,Kp,Vp=H(()=>{nD=[{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||""}
|
|
171
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];rD=5e3,ix=5,cx=3600*1e3,Kp=new Map});function Rs(e){return typeof e=="string"?e.trim():""}var dx=H(()=>{});import{execFile as sD}from"node:child_process";import{promisify as aD}from"node:util";async function qe(e,t,{input:n}={}){let r=Yp("git",["-C",e,...t],{maxBuffer:fx});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Os(e){if(!e)return!1;try{let{stdout:t}=await qe(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function px(e){let{stdout:t}=await qe(e,["rev-parse","--show-toplevel"]);return t.trim()}async function iD(e){try{return await qe(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Cs(e,t="bostrat: subtask snapshot"){let{stdout:n}=await qe(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Yp("git",["-C",e,...a],{maxBuffer:fx,env:o,...i});try{let a=await iD(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 Yp("rm",["-f",r])}catch{}}}async function Jp(e,t,{trailers:n=[]}={}){let{stdout:r}=await qe(e,["status","--porcelain"]);return r.trim()?(await qe(e,["add","-A"]),await qe(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function mi(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await qe(e,["status","--porcelain"]),r=n.split(`
|
|
172
|
+
`).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 hi(e,t){if(!t)return 0;try{let{stdout:n}=await qe(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Ms(e,t,n,r){return await qe(e,["worktree","add","--no-track","-b",n,t,r]),t}async function mx(e,t){try{return await qe(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function Ul(e,t,n){return await qe(e,["worktree","add",t,n]),t}async function Ns(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await qe(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 qe(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Xp(e){await qe(e,["init","-q"]),await qe(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function $t(e,t){try{await qe(e,["worktree","remove","--force",t])}catch{try{await qe(e,["worktree","prune"])}catch{}}}function cD(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 Zp(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Qp(e,t,n,r){let{stdout:o}=await qe(e,Zp(["diff","--shortstat",t,n],r));return cD(o)}async function em(e,t,n,r){let{stdout:o}=await qe(e,Zp(["diff","--name-only",t,n],r));return o.split(`
|
|
173
|
+
`).map(s=>s.trim()).filter(Boolean)}async function tm(e,t,n,r){let{stdout:o}=await qe(e,Zp(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await zp(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await zp(e,o,["--3way","--whitespace=nowarn"]),a=await lD(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await zp(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function zp(e,t,n){try{return await qe(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function lD(e){try{let{stdout:t}=await qe(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
174
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Yp,fx,Hl=H(()=>{Yp=aD(sD),fx=64*1024*1024});function Wl(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!uD.has(o.state)&&o.state!=="stopped"&&(o.state="stopped",r=!0);let s=Array.isArray(o.epochs)?o.epochs:[],a=s[s.length-1];a&&typeof a=="object"&&a.endedAt==null&&(a.endedAt=t,a.endReason==null&&(a.endReason=n),r=!0)}return r}var Ge,uD,bn=H(()=>{Ge=e=>!!(e&&(e.archivedAt||e.transferredTo)),uD=new Set(["merged","conflict","canceled","failed","handed-off"])});import nm from"node:fs/promises";import wx from"node:path";import dD from"node:os";function kx(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function xx(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function vx(e){let t=new Date(kx(e));return t.setDate(t.getDate()+7),t.getTime()}function Sx(e){let t=new Date(xx(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Gl(e=Date.now()){for(let[t,n]of Object.entries(Ps)){let r=n.startOf(e);if(r!==Un[t]){Un[t]=r;for(let o of bo.values())o[t]=0;for(let o of Ds)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Ds.delete(o)}}}function Tx(e,t){let n=rn?.caps?.[e]?.[Ps[t].capKey];return Number.isFinite(n)&&n>0?n:null}function ko(e,t=Date.now()){if(!e||!om||!rn?.caps)return null;Gl(t);let n=bo.get(e);for(let r of Object.keys(Ps)){let o=Tx(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:ql(s),resetsAt:Ps[r].nextStart(t)}}return null}function Ax(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${Un[t.window]}`;return Ds.has(n)?!1:(Ds.add(n),!0)}function hx(e,t,n,r=Date.now()){if(!e||!t)return;Gl(r);let o=fD(e);for(let[s,a]of Object.entries(Ps))n>=Un[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function pD(e,t,n=Date.now()){if(!rn?.caps||!rm)return;let r=bo.get(e);if(r)for(let[o,s]of Object.entries(Ps)){let a=Tx(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*bx||i>=a)continue;let c=`warn:${e}:${o}:${Un[o]}`;if(!Ds.has(c)){Ds.add(c);try{let l=await rm(t);if(!l)continue;await $(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:ql(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Ex(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 Kl(e,{source:t="local",updatedAt:n=Date.now()}={}){rn={caps:Ex(e),updatedAt:n,source:t};try{await nm.mkdir(gx,{recursive:!0}),await nm.writeFile(yx(),JSON.stringify(rn,null,2))}catch{}return wi()}function wi(e=Date.now()){Gl(e);let t=new Set([...bo.keys(),...Object.keys(rn?.caps||{})]),n={};for(let r of t){let o=bo.get(r);n[r]={weeklyUsd:ql(o?.weekly||0),monthlyUsd:ql(o?.monthly||0)}}return{caps:rn?.caps||{},configured:!!rn,seeded:om,updatedAt:rn?.updatedAt??null,spent:n,windows:{weekly:{start:Un.weekly,resetsAt:vx(e)},monthly:{start:Un.monthly,resetsAt:Sx(e)}},warnPct:bx}}async function _x({listTracks:e,getTrack:t}={}){rm=t||null,Gl();try{let o=JSON.parse(await nm.readFile(yx(),"utf8"));rn={caps:Ex(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{rn=null}let n=Date.now();lt.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ut(o);hx(o.provider||"unknown",s,o.ts??Date.now()),pD(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Un.weekly,Un.monthly);for(let o of await e?.()||[]){let s;try{s=await W(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||hx(a.provider||"unknown",Ut(a).usd,i,n)}}return om=!0,wi()}var gx,yx,bx,Ps,rn,om,Un,bo,Ds,rm,fD,ql,Vl=H(()=>{ge();Er();gx=process.env.BOSTRAT_STATE_DIR||wx.join(process.env.HOME||dD.tmpdir(),".bostrat"),yx=()=>wx.join(gx,"usage-caps.json"),bx=.8;Ps={weekly:{startOf:kx,nextStart:vx,capKey:"weeklyUsd"},monthly:{startOf:xx,nextStart:Sx,capKey:"monthlyUsd"}},rn=null,om=!1,Un={weekly:0,monthly:0},bo=new Map,Ds=new Set,rm=null,fD=e=>{let t=bo.get(e);return t||(t={weekly:0,monthly:0},bo.set(e,t)),t};ql=e=>Math.round(e*1e4)/1e4});import mD from"node:fs/promises";import hD from"node:os";import Ix from"node:path";function Px(e){return e==="codex"?bD:e==="cursor"?Nx:Cx}function $x(e){let t=String(e||"").split(`
|
|
175
|
+
`),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 Rx(e){let t=String(e||"").split(`
|
|
176
|
+
`),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 kD(e){let t=String(e||"").split(`
|
|
177
|
+
`);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 Ox(e){let t=String(e||"").split(`
|
|
178
|
+
`),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 Dx(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&&!Mx.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=Nx.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!yD.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function gi(e,t,{timeoutMs:n=wD}={}){let r=Date.now()+n;for(;;){let o=await De(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await zl(gD)}}async function xD(e,t){return await tt(e,`/model ${t.model}`,gn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function vD(e,t){await ml(e),await tt(e,"/model",{enter:!1}),await zl(250),await He(e,["Enter"],{kind:"model-switch"});let n=await gi(e,$x);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await He(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=kD(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await tt(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await gi(e,i=>Rx(i)||!$x(i)&&o(i)&&{done:o(i)});if(!s)throw await He(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?Mx.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 He(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await tt(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await gi(e,i=>!Rx(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 SD(){let e=Ix.join(process.env.CURSOR_CONFIG_DIR||Ix.join(hD.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await mD.readFile(e,"utf8"))?.model||null}catch{return null}}async function TD(e,t){await ml(e),await wl(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await gi(e,Ox),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 gi(e,Ox,{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 He(e,["Down"],{kind:"model-switch"}),await zl(250)}if(!n||!s||!o(s)){await He(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await He(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 He(e,["Enter"],{kind:"model-switch"}),await zl(800);let a=await SD();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 Bx(e,t,n){return e==="codex"?vD(t,n):e==="cursor"?TD(t,n):xD(t,n)}var zl,wD,gD,Cx,yD,bD,Mx,Nx,jx=H(()=>{ut();ri();zl=e=>new Promise(t=>setTimeout(t,e)),wD=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,gD=300,Cx=[{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"}],yD=new Set([...Cx.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"]),bD=[{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"}],Mx=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Nx=[{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 Jl(e){let t=e||{},n=AD(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function AD(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:yt(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:yt(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:yt(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:yt(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:yt(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:yt(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:yt(e.agent),payload:{...$r(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:yt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...$r(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:yt(e.agent),payload:{change:"subtask-resumed",...$r(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",...$r(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...$r(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...$r(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:yt(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:yt(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:yt(e.agent),payload:{...$r(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...$r(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:yt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function Ux(e,t){let n=Jl(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;am(()=>$(r,n))}async function Hx(e){if(!(!e?.id||Lx.has(e.id))){Lx.add(e.id);try{let t=await W(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=Jl(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(e,o)}}catch{}}}function $r(e){let{type:t,at:n,...r}=e;return r}function Wx(e,t,n){!n?.promptId||sm.get(e.id)===n.promptId||(sm.set(e.id,n.promptId),am(()=>$(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:yt(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 im(e,t,n,r={}){sm.delete(e.id),am(()=>$(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:yt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var yt,Yl,am,Fx,Lx,sm,cm=H(()=>{ge();yt=e=>e||null;Yl=Promise.resolve(),am=e=>(Yl=Yl.then(e).catch(()=>{}),Yl),Fx=()=>Yl;Lx=new Set;sm=new Map});function RD(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Yx(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of RD(n).reverse()){if(!ED.test(r))continue;let o=r.endsWith("?")&&r.length<=$D&&ID.test(r);if(_D.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function Xx(e){let t=String(e||""),n=[];OD.test(t)&&n.push("commit"),Jx.test(t)&&n.push("rebase"),CD.test(t)&&n.push("push");let r=ND.test(t);!r&&MD.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 Zx(e){return Jx.test(String(e||""))}function Bs(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)`:"",h=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${h}${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${h}${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 Qx(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 ev(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<=PD&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function tv(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+DD||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function nv(e){let t=(Array.isArray(e)?e:[]).filter(r=>qx[r]).map(r=>qx[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 Gx,Kx,Vx,zx,ED,_D,ID,$D,Rr,OD,Jx,CD,MD,ND,PD,DD,qx,xo=H(()=>{Gx=/(?<!\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,Kx=/(?<!\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,Vx=/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,zx=/(?<!\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,ED=new RegExp(`\\b(?:${Gx}|${Kx}|${Vx}|${zx})\\b`,"i"),_D=/\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,ID=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,$D=90;Rr=["commit","rebase","push","pr"],OD=new RegExp(`\\b(?:${Gx})\\b`,"i"),Jx=new RegExp(`\\b(?:${zx})\\b`,"i"),CD=new RegExp(`\\b(?:${Kx})\\b`,"i"),MD=new RegExp(`\\b(?:${Vx})\\b`,"i"),ND=/\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;PD=3e4;DD=2500;qx={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import BD from"node:crypto";import Xl from"node:fs/promises";import rv from"node:path";import jD from"node:os";async function Ke(){return Or||(lm??=Xl.readFile(dm,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Or=e,!t&&je()){let{records:n,source:r}=await rt("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;Or[ie(i.repo,i.number)]=i}Object.keys(Or).length&&await fm().catch(()=>{})}}return Or}).finally(()=>lm=null),lm)}async function fm(){let e=JSON.stringify({links:Or},null,2),t=`${dm}.${process.pid}.tmp`;await Xl.mkdir(ov,{recursive:!0}),await Xl.writeFile(t,e),await Xl.rename(t,dm)}async function nt(e){um=um.then(fm,fm),await um.catch(()=>{});let t=Or?.[e];t&>("prlinks",{id:sv(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Rt({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 Ke(),c=ie(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:LD.has(a),firstSeen:Date.now()},await nt(ie(e,t)),{link:i[c],created:!0})}async function Hn(e,t){if(!e||t==null)return null;let n=(await Ke())[ie(e,t)];return!n||n.announced?null:(n.announced=!0,await nt(ie(e,t)),n)}async function ot(e,t){return!e||t==null?null:(await Ke())[ie(e,t)]??null}async function Cr(e,t,n){if(!e||t==null)return null;let r=(await Ke())[ie(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,delete r.followupPending,await nt(ie(e,t)),r):null}async function av(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ke())[ie(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await nt(ie(e,t)),o):null}async function iv(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ke())[ie(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await nt(ie(e,t)),o):null}async function Zl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ke())[ie(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await nt(ie(e,t)),r)}async function Ql(e,t){if(!e||t==null)return null;let n=(await Ke())[ie(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await nt(ie(e,t)),n)}async function eu(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ke())[ie(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await nt(ie(e,t)),r)}async function tu(e,t){if(!e||t==null)return null;let n=(await Ke())[ie(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await nt(ie(e,t)),n)}async function cv(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ke())[ie(e,t)];return r?(r.baseFoldAt=n,await nt(ie(e,t)),r):null}async function lv(e,t){if(!e||t==null)return null;let n=(await Ke())[ie(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await nt(ie(e,t)),n)}async function uv(e,t,n){if(!e||t==null||!n)return null;let r=(await Ke())[ie(e,t)];return r?(r.lastHeadSha=n,await nt(ie(e,t)),r):null}async function dv(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Ke())[ie(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 nt(ie(e,t))),{prev:o,link:r}}async function yi(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ke())[ie(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await nt(ie(e,t)),r):null}async function vo(e,t,n=!0){if(!e||t==null)return null;let r=(await Ke())[ie(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await nt(ie(e,t)),r):null}async function bi(e,t,n=!0){if(!e||t==null)return null;let r=(await Ke())[ie(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await nt(ie(e,t)),r):null}async function fv(e,t,n){if(!e||t==null)return null;let r=(await Ke())[ie(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 nt(ie(e,t)),r}async function pv(e,t,n){if(!e||t==null)return null;let r=(await Ke())[ie(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).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.followupPending=[...o.values()],await nt(ie(e,t)),r}async function ki(e,t){if(!e||t==null)return null;let n=(await Ke())[ie(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 nt(ie(e,t)),n)}async function mv(){return Object.values(await Ke()).filter(e=>!FD.has(e.state))}async function So(){return Object.values(await Ke()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function hv(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await rt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ke(),r=0;for(let[o,s]of Object.entries(n))await gt("prlinks",{id:sv(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var ov,dm,ie,sv,Or,lm,um,LD,FD,Wn=H(()=>{mr();ov=process.env.BOSTRAT_STATE_DIR||rv.join(process.env.HOME||jD.tmpdir(),".bostrat"),dm=rv.join(ov,"pr-links.json"),ie=(e,t)=>`${e}#${Number(t)}`,sv=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=BD.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Or=null,lm=null,um=Promise.resolve();LD=new Set(["api","observed","reconciled"]);FD=new Set(["merged","closed","lost"])});import UD from"node:fs";import pm from"node:fs/promises";import bv from"node:path";import HD from"node:os";function vv(e,t=!1){if(!t&&e-gv<WD)return;gv=e;let n=JSON.stringify({windowStart:js,byCaller:Object.fromEntries(qn),lastHour:Mr});yv=yv.then(()=>pm.mkdir(kv,{recursive:!0})).then(()=>pm.writeFile(`${nu}.tmp`,n)).then(()=>pm.rename(`${nu}.tmp`,nu)).catch(()=>{})}function Sv(e){e-js<xv||(Mr={start:js,total:ru(qn),byCaller:Object.fromEntries(qn)},Mr.total&&console.log(`[github] ${Mr.total} calls last hour`,Mr.byCaller),js=e,qn=new Map,vv(e,!0))}function Gn(e="other",t=Date.now()){Sv(t),qn.set(e,(qn.get(e)||0)+1),vv(t)}function Tv(e=Date.now()){return Sv(e),{windowStart:js,total:ru(qn),byCaller:Object.fromEntries(qn),lastHour:Mr,backoffUntil:To>e?To:null}}function Nr(e){return qD.test(String(e?.message||e||""))}function Pr(e,t=Date.now()){return To<=t&&console.warn(`[github] rate limited \u2014 backing off ${wv/6e4} min:`,String(e?.message||e||"").slice(0,200)),To=Math.max(To,t+wv),To}function ou(e=Date.now()){return Math.max(0,To-e)}var kv,nu,xv,wv,WD,js,qn,Mr,To,gv,ru,yv,qD,xi=H(()=>{kv=process.env.BOSTRAT_STATE_DIR||bv.join(process.env.HOME||HD.tmpdir(),".bostrat"),nu=bv.join(kv,"github-metrics.json"),xv=60*6e4,wv=5*6e4,WD=6e4,js=Date.now(),qn=new Map,Mr=null,To=0,gv=0,ru=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(UD.readFileSync(nu,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<xv?(js=e.windowStart,qn=new Map(Object.entries(e.byCaller||{})),Mr=e.lastHour||null):ru(new Map(Object.entries(e.byCaller||{})))&&(Mr={start:e.windowStart,total:ru(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}yv=Promise.resolve();qD=/rate limit|too quickly|abuse detection|HTTP 429/i});import Av from"node:fs/promises";import Ev from"node:path";import GD from"node:os";function au({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};su.push(l),su.length>VD&&su.shift();let u=mm.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)),mm.set(e,u),zD(l),l}async function zD(e){try{await Av.mkdir(_v,{recursive:!0}),await Av.appendFile(KD,JSON.stringify(e)+`
|
|
179
|
+
`)}catch{}}function Iv(){let e={};for(let[t,n]of mm)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:su.slice(-20)}}var _v,KD,VD,su,mm,iu=H(()=>{_v=process.env.BOSTRAT_STATE_DIR||Ev.join(process.env.HOME||GD.tmpdir(),".bostrat"),KD=Ev.join(_v,"github-latency.ndjson"),VD=100,su=[],mm=new Map});import{execFile as YD}from"node:child_process";import{promisify as JD}from"node:util";async function XD(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Fs("git",["-C",e,"rev-parse","HEAD"]),Fs("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 Ht(e,t,n={}){let{caller:r="prs",...o}=n,s=await pn({cwd:e});Gn(r);try{let{stdout:a}=await Fs("gh",t,{cwd:e,...ZD,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Nr(a)&&Pr(a),a}}async function gm(){if(cu&&Date.now()-cu.at<6e4)return cu.data;let e;try{Gn("auth");let t=await Fs("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 Oa();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return cu={at:Date.now(),data:e},e}async function QD(e){try{let{stdout:t}=await Fs("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Rv(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 hm(e){let t=(e||[]).map(Rv),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 bm(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function Cv(e,t){if(!t)return null;let n=await Ee(e);if(!n)return null;try{let r=await Ht(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function Mv(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 Nv(e,t){let n=await Cv(e,t);return n===null?{...hm([]),unavailable:!0}:hm(Mv(n))}function lu(e,t=hm(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",state:e.state??null,mergedAt:e.mergedAt??null,closedAt:e.closedAt??null,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 t1(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Ls.delete(s);continue}r.add(s),Ls.has(s)||Ls.set(s,n),o.mergeabilityPending=n-Ls.get(s)<e1}for(let o of Ls.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Ls.delete(o);return t}async function Us(e,t){let n=await Kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Ht(n.path,["pr","view",String(r),"--json",ym]);return lu(JSON.parse(o))}catch(o){if(!bm(o))throw o;let s=await Ht(n.path,["pr","view",String(r),"--json",Ov]),a=JSON.parse(s);return lu(a,await Nv(n.path,a.headRefOid))}}async function kn(e,{caller:t="prs"}={}){try{let n=await Ht(e,["pr","list","--json",ym,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>lu(r)).sort($v)}catch(n){if(!bm(n))throw n;let r=await Ht(e,["pr","list","--json",Ov,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>lu(o,await Nv(e,o.headRefOid))))).sort($v)}}function Wt(){vi.clear()}async function o1(e,t){if(!e)return t;for(let n of t){let r=await ot(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 Zl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await eu(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function Pv(){let e=await gm();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await Pn())await QD(a.path)&&t.push(a);let n=Date.now(),r=ou(n)>0,o=await Promise.all(t.map(async a=>{let i=vi.get(a.path),c=i?.pending?r1:n1;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Ee(a.path).catch(()=>null),u=t1(a.path,await kn(a.path,{caller:"tab"}),n);if(await o1(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&au({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 vi.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:xn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of vi.keys())s.has(a)||vi.delete(a);return{auth:e,repos:o}}async function Kn(e){let t=(await Pn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Ao(e){return rs(e)}async function Hs(e,t,{method:n="squash"}={}){let r=await Kn(e);if(!Si.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 pn({cwd:r.path})).GH_TOKEN){let a=await lf().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ht(r.path,s)}catch(a){throw new Error(xn(a))}finally{Wt()}return{ok:!0,number:o}}async function Ti(e,t){let n=await Kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ht(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(xn(s))}finally{Wt()}let o=await Ee(n.path).catch(()=>null);return o&&await cv(o,r).catch(()=>{}),{ok:!0,number:r}}async function km(e,t){let n=await Kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ht(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(xn(o))}finally{Wt()}return{ok:!0,number:r}}function xm(e){return e.green&&!e.isDraft}function c1(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 Dv(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:xm(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Bv(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Ai(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function jv(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Ai))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function Lv(){let e={};for(let[t,n]of wm)e[t]={...n};return e}async function Fv(e,{method:t="merge",onMerged:n=null}={}){let r=await Kn(e);if(!Si.has(t))throw new Error(`invalid merge method: ${t}`);if(wm.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await kn(r.path)).filter(xm).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 wm.set(r.path,s),u1(s,o,{listPrs:()=>kn(r.path),merge:a=>Hs(r.path,a,{method:t}),sleep:i1,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function l1(e,t){let n=Date.now()+(t.settleMaxMs??a1),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(s1)}}async function u1(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 l1(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: ${xn(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(!xm(a)){e.skipped.push({number:a.number,title:a.title,reason:c1(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:xn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Wt()}}async function vm(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Fs("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${xn(l)}`)}await XD(e);let o=n||"";if((await pn({cwd:e})).GH_TOKEN){let l=await lf().catch(()=>null);l&&(o=`${o?`${o}
|
|
180
|
+
|
|
181
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await Ht(e,s)}catch(l){throw new Error(xn(l))}finally{Wt()}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 Uv(e,t){let n=await Kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Ht(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(xn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!bm(i))throw i;let c=await Ht(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Cv(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(xn(i));o=Mv(l).map(Rv).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=d1(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Ht(n.path,["run","view",c,"--log-failed"]).then(u=>f1(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function d1(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function f1(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
182
182
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
183
183
|
`+o.slice(-t).join(`
|
|
184
184
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
185
|
-
`+r.slice(-n)),r}function
|
|
186
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
187
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+UD(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 im(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function xv(e){let t=bv(e),n=t.match(jD);if(!n)return null;let r=im(t,n.index);return FD.test(r)?null:{repo:r.match(kv)?.[1]??null,head:r.match(HD)?.[1]??null}}function vv(e){let t=bv(e),n=t.match(LD);if(!n)return null;let r=im(t,n.index),o=im(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("-")){qD.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(kv)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function GD(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Hn("observer");let{stdout:c}=await hv("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",KD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Rr(l)&&Or(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-PD&&!n(c)),s=await ln({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 VD(e,t){if(t)return[t];let n=[];for(let r of Sv(e)){let o=await Se(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await On().catch(()=>[])){let o=await Se(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Sv(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 zD(e,t,n){let r=await js(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)||WD(c.headRefName,e.id),i=[];for(let c of await VD(e,t?.repo??null)){let l;try{l=await GD(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await tt(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 Tv(e,t,{delays:n=wv,turnEnd:r}={}){let o=xv(t.command);if(!o)return null;let s=!!vv(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Mr.has(a))return null;Mr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await zD(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:h}=await Ot({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!h&&!(p?.trackId===e.id&&await Ln(f,d.number))?null:(await am(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{Mr.delete(a)}})();return c.catch(()=>Mr.delete(a)),c}function Ev(e,t,{delays:n=wv,turnEnd:r,openedWait:o}={}){let s=vv(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Mr.has(a))return null;Mr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let h=Sv(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let g=await Se(w).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=(await js(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(h,w)=>{let g=await tt(c,h.number);return g&&g.trackId!==e.id?null:(g||await Ot({repo:c,number:h.number,trackId:e.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await mi(c,h.number),w&&((await F(e).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===h.number)||(await am(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:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},p=null;for(let h of n){await new Promise(T=>setTimeout(T,h));let w=[];for(let T of f)try{Hn("observer");let{stdout:_}=await hv("gh",["pr","view",String(l),"--repo",T,"--json",YD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await ln({slug:T})}}),O=JSON.parse(_||"null");O?.number&&w.push({slug:T,pr:O})}catch(_){Rr(_)&&Or(_)}let g=w.find(T=>T.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!g)continue;c=g.slug;let k=g.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return d(k,!0);continue}let S=await tt(c,k.number);if(S&&S.trackId!==e.id)return null;S||await Ot({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await am(t.box,t.agent,r);let E=await F(e).catch(()=>[]);if(E.some(T=>T.type==="pr.state.changed"&&T.payload?.change==="merged"&&T.payload?.number===k.number))return null;let R=T=>T.some(_=>_.type==="pr.state.changed"&&_.payload?.change==="opened"&&_.payload?.number===k.number),B=0;if(!R(E)){if(xv(t.command)&&await Ln(c,k.number)){let T=Date.now();B=T+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:T,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await tt(c,k.number))?.announced){let T=o?.capMs??DD,_=o?.pollMs??BD,O=Date.now()+T;for(;Date.now()<O&&(await new Promise(I=>setTimeout(I,_)),!R(await F(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(),B),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Kn(c,k.number).catch(()=>{}),await qn(c,k.number).catch(()=>{}),await Ir(c,k.number,"merged"),k}return p?d(p,!1):null}finally{Mr.delete(a)}})();return i.catch(()=>Mr.delete(a)),i}var hv,wv,MD,ND,PD,DD,BD,gv,yv,jD,LD,FD,mv,UD,kv,HD,WD,qD,KD,Mr,YD,Av=j(()=>{me();ar();it();gi();Fn();Nr();Si();Eo();hv=CD(OD),wv=[4e3,15e3,45e3],MD=2500,ND=5*6e4;PD=15e3,DD=9e4,BD=2e3,gv=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,yv=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,jD=new RegExp(`${gv}${yv}create\\b`),LD=new RegExp(`${gv}${yv}merge\\b`),FD=/\s--web\b/,mv=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,UD=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");kv=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,HD=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,WD=pv;qD=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);KD="number,url,title,headRefName,createdAt";Mr=new Set;YD="number,url,title,headRefName,state"});import JD from"node:fs/promises";import lm from"node:path";import XD from"node:os";function cm(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function iu(e){let t=ZD.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=cm(n,"from"),s=o?o.replace(/^uds:/,""):null,a=_v(s);return{socket:s,pid:a,name:cm(n,"from-name"),mode:cm(n,"from-mode"),text:r.trim()}}function _v(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function QD(){return process.env.CLAUDE_CONFIG_DIR?lm.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):lm.join(XD.homedir(),".claude","sessions")}async function eB(e){let t=_v(e);if(!t)return null;try{let n=await JD.readFile(lm.join(QD(),`${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 Iv(e,t){let n=await eB(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 ZD,um=j(()=>{ZD=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function dm(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function fm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!dm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),$v);try{let a=await Rv(`${Ie(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 Ov(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(!dm(s))return{ok:!1,source:"unconfigured"};let i=cu.get(e.id)||0;if(a()-i<tB)return{ok:!1,source:"debounced"};cu.set(e.id,a());let c=await Ti({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return cu.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 fm({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||cu.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function Ti({env:e=process.env}={}){if(!dm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),$v);try{let r=await Rv(`${Ie(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 $v,Rv,tB,cu,pm=j(()=>{it();$v=1e4,Rv=(...e)=>fetch(...e);tB=10*6e4,cu=new Map});function Cv(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Mv(e,t,n=mm){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await rB(`${Ie(t)}${e}`,{headers:oB(t),signal:r.signal})}finally{clearTimeout(o)}}async function Ei({env:e=process.env,force:t=!1,timeoutMs:n=mm}={}){if(!Cv(e))return{followups:[],source:"unconfigured"};if(!t&&Ao&&Date.now()-Ao.at<nB)return{followups:Ao.followups,source:"cache"};try{let r=await Mv("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return Ao={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Ao?{followups:Ao.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function Nv(e,{env:t=process.env,timeoutMs:n=mm}={}){if(!e)return{followup:null,source:"unconfigured"};if(!Cv(t))return{followup:null,source:"unconfigured"};try{let r=await Mv(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=Ao?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var nB,mm,Ao,rB,oB,Pv=j(()=>{it();nB=6e4,mm=5e3,Ao=null,rB=(...e)=>fetch(...e);oB=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import{randomUUID as Fs,createHash as sB}from"node:crypto";function Lv(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function uB(e){for(let[t,n]of lB)if(t.test(String(e||"")))return`a standing prompt must not embed this Track's specifics (${n}) \u2014 the card supplies them at fire time; arm a one-shot with \`offer\` instead`;return null}function Fv(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Ls(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>Dv)throw new Error(`label must be \u2264${Dv} characters`);let o=Ls(n.trigger)||(n.ship?"now":""),s=t?iB:jv.filter(u=>u!=="track.opened");if(!s.includes(o))throw new Error(`trigger must be one of ${s.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let a=n.ship&&o==="now"?fB(n.ship):null,i=Ls(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>Bv)throw new Error(`prompt must be \u2264${Bv} bytes`);if(t){let u=uB(i);if(u)throw new Error(u)}let c=Ls(n.why).slice(0,aB)||null,l=t?dB(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function dB(e){if(e==null||e===""||e==="*")return null;let t=String(e).trim();if(!/^[\w.-]+\/[\w.-]+$/.test(t))throw new Error(`repo must be a forge slug like owner/name, got "${e}"`);return t}function fB(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):hm.slice();for(let i of n)if(!hm.includes(i))throw new Error(`ship.actions may only contain ${hm.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=Ls(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>Ls(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function lu(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}async function Uv(e,t,{pull:n=Ei,ship:r={}}={}){if(ym(e))throw new Error("follow-ups don't fire on a loop's run Track \u2014 report the outcome with `bostrat-loop report` instead");let o=Fv(t);if(o.ship)return{kind:"ship",...await hB(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null};if(o.trigger==="now"){let g=Fs(),k=Fs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:g,trigger:"now",label:o.label,prompt:o.prompt,why:o.why}}),await $(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:k,trigger:"now",repo:e.workspace?.repo||null,context:{},rows:[{armId:g,label:o.label,prompt:o.prompt,promptHash:wm(o.prompt),origin:"agent",why:o.why}]}}),{kind:"now",armId:g,offerId:k,...o}}let c=e.workspace?.repo||null,{followups:l}=await n().catch(()=>({followups:[]})),u=Lv(o.label),f=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(f)return{kind:"covered",covered:{id:f.id,label:f.label},...o};let d=await F(s),p=lu(d,{trigger:o.trigger}),h=p.length?p[p.length-1].armId:null,w=Fs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:w,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...h?{replaces:h}:{}}}),{kind:"armed",armId:w,replaces:h,...o}}async function Hv(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await F(n);if(!lu(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await $(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function Wv(e,t,{pull:n=Ei}={}){if(ym(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=Fv(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await F(s)).reduce((g,k)=>k.type==="followup.create.offered"?k.payload?.proposalId??g:k.type==="followup.create.answered"&&k.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=Lv(r.label),f=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),d=f.find(g=>g.status==="deleted"),p=f.find(g=>g.status!=="deleted");if(p)throw new Error(`"${p.label}" (${gm[p.trigger]||p.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(d){let g=d.deletedAt?new Date(d.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${d.label}" on ${g} \u2014 do not re-propose it`)}let h=Fs(),w=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await $(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:w?.agent??null,payload:{ts:Date.now(),proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function qv(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await F(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await $(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Ai(e,t,n={},{pull:r=Ei}={}){if(!e||ym(e))return null;let o={id:e.id,cwd:e.cwd},s=await F(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null,i=lu(s,{trigger:t}).map(d=>({armId:d.armId,label:d.label,prompt:d.prompt,promptHash:wm(d.prompt),origin:"agent",why:d.why??null})),c=[];if(t!=="now"){let{followups:d}=await r().catch(()=>({followups:[]}));c=d.filter(p=>p.status==="active"&&p.mode!=="off"&&p.trigger===t&&(p.repo==null||p.repo===a)).sort((p,h)=>{let w=g=>g.stats?.offered?(g.stats.accepted||0)/g.stats.offered:0;return w(h)-w(p)||(p.origin==="operator"?-1:0)-(h.origin==="operator"?-1:0)||(h.createdAt||0)-(p.createdAt||0)}).map(p=>({followupId:p.id,label:p.label,promptPreview:pB(p.prompt),promptHash:wm(p.prompt),origin:p.origin,repo:p.repo??null}))}let l=[...i,...c].slice(0,cB);if(!l.length)return null;let u=Fs(),f={ts:Date.now(),offerId:u,trigger:t,repo:a,context:{...n,repo:a},rows:l};return await $(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:f}),f}function mB(e){let t=e?.context||{},n=Array.isArray(t.prs)&&t.prs.length?t.prs:t.prNumber?[{number:t.prNumber,title:t.prTitle,url:t.prUrl,repo:t.repo}]:[],r=o=>`PR #${o.number}${o.title?` "${o.title}"`:""}${o.url?` (${o.url})`:""}${o.repo?` on ${o.repo}`:""}`;switch(e?.trigger){case"pr.merged":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} merged:`:"Follow-up the operator tapped after this Track's PR merged:";case"pr.opened":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} opened:`:"Follow-up the operator tapped after this Track's PR opened:";case"checks.green":return n.length?`Follow-up the operator tapped after CI went green on ${n.map(r).join(" and ")}:`:"Follow-up the operator tapped after CI went green:";case"ship.done":return"Follow-up the operator tapped after the ship you just ran completed:";default:return"Follow-up the operator tapped:"}}async function Kv(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=Nv}=s;if(!t)throw new Error("offerId required");let l={id:e.id,cwd:e.cwd},u=await F(l),f=u.find(E=>E.type==="followup.offered"&&E.payload?.offerId===t);if(!f)throw new Error("unknown offer");if(u.some(E=>E.type==="followup.answered"&&E.payload?.offerId===t))throw new Error("offer already answered");let d=f.payload.rows||[],p=E=>$(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...E}});if(!n&&!r)return await p({action:"dismiss",followupIds:d.map(E=>E.followupId).filter(Boolean),armIds:d.map(E=>E.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let h=null,w=null;if(n){if(!d.some(B=>B.followupId===n))throw new Error("that follow-up is not on this card");let{followup:E,source:R}=await c(n);if(R==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!E||E.status!=="active"){await p({action:"removed",followupId:n});let B=new Error("removed in Settings since it was offered");throw B.code="removed",B}h=E.prompt,w=E.label}else{let E=d.find(B=>B.armId===r);if(!E)throw new Error("that arm is not on this card");if(h=u.find(B=>B.type==="followup.armed"&&B.payload?.armId===r)?.payload?.prompt??E.prompt,w=E.label,!h)throw new Error("the arm's prompt is missing from the log")}let g=!1;if(e.archivedAt){if(!i)throw new Error("this Track is archived");await i(e.id,{freshSession:!0}),g=!0}if(!a)throw new Error("sayToLead unavailable");let k=`${mB(f.payload)}
|
|
185
|
+
`+r.slice(-n)),r}function xn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
186
|
+
`).find(n=>n.trim())||"gh failed"}var Fs,ZD,cu,ym,Ov,e1,Ls,$v,n1,r1,vi,Si,s1,a1,i1,wm,Eo=H(()=>{ur();ct();Wn();xi();iu();ge();Fs=JD(YD),ZD={timeout:3e4,maxBuffer:8*1024*1024};cu=null;ym="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",Ov=ym.replace("statusCheckRollup,","");e1=9e4,Ls=new Map;$v=(e,t)=>e.number-t.number;n1=3e4,r1=1500,vi=new Map;Si=new Set(["squash","merge","rebase"]);s1=2500,a1=9e4,i1=e=>new Promise(t=>setTimeout(t,e));wm=new Map});import{execFile as p1}from"node:child_process";import{promisify as m1}from"node:util";async function w1(e,t){try{let{stdout:n}=await h1("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function g1(e,t,n,{rebase:r=!1,listPrs:o=kn}={}){let s=await mi(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await hi(e.dir,n).catch(()=>0),i=t?await w1(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let f=s||a>0,d=[];s&&d.push("commit"),r&&d.push("rebase"),(s||c)&&d.push("push"),f&&(u!=="open"||s||c)&&d.push("pr");let p=d.filter(h=>!(h==="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:uu.filter(h=>d.includes(h)),defaults:uu.filter(h=>p.includes(h))}}async function du(e,{branch:t,rebase:n=!1,listPrs:r=kn}={}){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 Os(s[0].dir).catch(()=>!1))return null;let a=[];for(let d of s)a.push(await g1(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=uu.filter(d=>c.some(p=>p.available.includes(d))),u=uu.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 h1,uu,Sm=H(()=>{Hl();Eo();h1=m1(p1),uu=["commit","rebase","push","pr"]});function fu(e,t,n){let{state:r,reason:o}=ep(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of Ei)a-c.at>=Hv()&&Ei.delete(i);Ei.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=Ei.get(e);return s?Date.now()-s.at>=Hv()?(Ei.delete(e),!1):s.busy:!1}async function Tm(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await De(e,{lines:et}).catch(()=>"");if(!fu(e,t,r))return dk(t,r)?{working:!0,prompted:!1}:n;if(yo(t,r)){let o=await jn(e).catch(()=>r),s=Ir({name:e,agent:t},o);if(s)return!Fl({name:e,agent:t},s,o)||ux(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function _o(e,t){return(await Tm(e,t)).working}var Hv,Ei,_i=H(()=>{ut();ni();Gp();Vp();Hv=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Ei=new Map});import{execFile as y1}from"node:child_process";import{promisify as b1}from"node:util";async function Dr(e){if(!e)return null;let t=Wv.get(e);if(t&&Date.now()-t.at<x1)return t.branch;let n=null;try{let{stdout:r}=await k1("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return Wv.set(e,{at:Date.now(),branch:n}),n}function qv(e){return String(e||"").match(v1)?.[1]??null}function S1(e,t){let n=qv(e);return!!n&&!!t&&n===t}function Gv(e,t){let n=qv(e);return!!n&&n!==t}function T1(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 Ws(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of T1(e))n(await Dr(r));return t}async function pu(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=>S1(t,a.id));if(s)return s;for(let a of r)if(!Ge(a)&&(await Ws(a)).includes(t))return a;return null}var k1,x1,Wv,v1,Io=H(()=>{bn();k1=b1(y1),x1=5e3,Wv=new Map;v1=/^track\/.+-([0-9a-f]{8})$/});import{execFile as A1}from"node:child_process";import{promisify as E1}from"node:util";function qs({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function hu(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function gu({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?C1[r]:null;return a&&s.push(a),s.push(`${Jv}: ${qs({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${Xv}: ${String(o).trim()}`),s}function Kv(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
187
|
+
`)){let r=n.trim(),o=/^([A-Za-z-]+):\s*(.+)$/.exec(r);if(!o)continue;let s=o[1].toLowerCase(),a=o[2].trim();if(s===Jv.toLowerCase()&&!t.epoch)t.epoch=hu(a);else if(s===Xv.toLowerCase()&&!t.model)t.model=a;else if(s===Zv.toLowerCase()&&!t.cause){let i=a.toLowerCase();Ii.includes(i)&&(t.cause=i)}else if(s==="co-authored-by"||s==="made-with"){let i=a.toLowerCase();t.provider||(i.startsWith("claude")?t.provider="claude":i.startsWith("codex")?t.provider="codex":i.startsWith("cursor")&&(t.provider="cursor"))}}return t}function M1(e,t=Br){return e<=t?"rework":"drift"}function N1(e,{window:t=Br,end:n=$m}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function yu({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return P1.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${Zv} trailer`}:B1.test(s)&&!Vv.test(s)?{cause:"style",level:"C",basis:"subject"}:Vv.test(s)?{cause:"defect",level:"C",basis:"subject"}:D1.test(s)?{cause:"design",level:"C",basis:"subject: refactor"}:n==="wrong-approach"?{cause:"design",level:"C",basis:"wall label: wrong-approach"}:r==="bug-fix"?{cause:"defect",level:"C",basis:"fixer prompt: bug-fix"}:null}function Om({heuristic:e=null,subject:t="",ref:n=null,promptKind:r=null,fixerCategory:o=null,ticketKind:s=null}={}){let a=String(t||""),i=j1.exec(a),c=i?i[1].toLowerCase():null;return e&&(e.level==="A"||e.level==="E")?{intent:"fix",basis:e.basis}:s&&/^bug/i.test(s)?{intent:"fix",basis:`linked ticket: ${s}`}:c&&L1.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&F1.has(c)?{intent:"planned",basis:`subject prefix: ${c}:`}:s&&/^(story|feature|task)/i.test(s)?{intent:"planned",basis:`linked ticket: ${s}`}:e?{intent:"likely-fix",basis:e.basis}:r==="followup-go"?{intent:"planned",basis:"operator-tapped follow-up"}:zv.test(String(n||""))||zv.test(a)?{intent:"planned",basis:"blueprint item"}:o&&U1.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function G1(e){let t=String(e||"");return H1.some(n=>n.test(t))?"excluded":W1.some(n=>n.test(t))?"docs":q1.some(n=>n.test(t))?"config":"code"}function K1(e){let t=[],n=null,r=null;for(let o of String(e||"").split(`
|
|
188
|
+
`)){if(o.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1,hunks:[]},r=null,t.push(n);continue}if(n)if(o.startsWith("--- ")&&!r){let s=o.slice(4).trim();n.oldPath=s==="/dev/null"?null:s.replace(/^a\//,"")}else if(o.startsWith("+++ ")&&!r){let s=o.slice(4).trim();n.newPath=s==="/dev/null"?null:s.replace(/^b\//,"")}else if(o.startsWith("Binary files"))n.binary=!0;else if(o.startsWith("@@")){let s=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(o);if(!s)continue;let a=Number(s[1]),i=s[2]==null?1:Number(s[2]),c=Number(s[3]),l=s[4]==null?1:Number(s[4]);i>0&&n.deleted.push([a,i]),n.added+=l,r={oldStart:a,oldCount:i,newStart:c,newCount:l,del:[],add:[]},n.hunks.push(r)}else r&&o.startsWith("-")?r.del.push(o.slice(1)):r&&o.startsWith("+")&&r.add.push(o.slice(1))}return t}function V1(e,t){let n=Im(e),r=Im(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>mu&&(n=n.slice(0,mu)),r.length>mu&&(r=r.slice(0,mu));let o=new Array(r.length+1),s=new Array(r.length+1);for(let a=0;a<=r.length;a++)o[a]=a;for(let a=1;a<=n.length;a++){s[0]=a;let i=n.charCodeAt(a-1);for(let c=1;c<=r.length;c++){let l=i===r.charCodeAt(c-1)?0:1;s[c]=Math.min(o[c]+1,s[c-1]+1,o[c-1]+l)}[o,s]=[s,o]}return 1-o[r.length]/Math.max(n.length,r.length)}function z1(e,t=Qv){let n=new Set,r=e?.del||[],o=e?.add||[];for(let s=0;s<r.length;s++){let a=e.oldStart+s;if(!Im(r[s]).length){n.add(a);continue}s<o.length&&V1(r[s],o[s])>=t&&n.add(a)}return n}function Y1(e){let t=[...e].sort((r,o)=>r-o),n=[];for(let r of t){let o=n[n.length-1];o&&o[0]+o[1]===r?o[1]+=1:n.push([r,1])}return n}function J1(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
189
|
+
`)){let o=/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(r);if(o){n={sha:o[1],line:Number(o[3]),time:null},t.set(n.line,n);continue}n&&r.startsWith("committer-time ")&&(n.time=Number(r.slice(15))*1e3)}return t}async function Yv(e,t){let{stdout:n}=await _m(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
190
|
+
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
191
|
+
`)}}async function eS(e,t,{fixKey:n=null,window:r=Br,resolveOrigin:o=null,context:s={}}={}){let a=await Yv(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=Kv(a.message),l=n?hu(n):c.epoch,u=l?qs(l):null,{stdout:f}=await _m(e,["diff","-U0","--no-color","-M","-C",i,t]),d=K1(f),p=0,h=0,w=0,g=0,b=0,v=0,x=0,I=new Map;for(let E of d){if(E.binary)continue;let q=E.oldPath||E.newPath||"",z=E.deleted.reduce((K,[,he])=>K+he,0),F=G1(q);if(F==="excluded"){w+=z;continue}if(F==="docs"){g+=z,b+=E.added;continue}if(F==="config"){v+=z;continue}p+=E.added;let R=new Set;for(let K of E.hunks||[])for(let he of z1(K))R.add(he);x+=R.size;let G=[];for(let[K,he]of E.deleted)for(let ce=K;ce<K+he;ce++)R.has(ce)||G.push(ce);if(h+=G.length,!E.oldPath||!G.length)continue;let X;try{let K=["blame","-w","-M","-C","--line-porcelain"];for(let[he,ce]of Y1(G))K.push("-L",`${he},${he+ce-1}`);K.push(i,"--",E.oldPath),X=J1((await _m(e,K)).stdout)}catch{continue}for(let{sha:K,time:he}of X.values()){let ce=I.get(K)??{lines:0,time:he};ce.lines+=1,I.set(K,ce)}}let P=[],B=0,j=new Map;for(let[E,{lines:q,time:z}]of I){let F=j.get(E);if(!F){try{F=await Yv(e,E)}catch{F=null}j.set(E,F)}let R=F?Kv(F.message):{epoch:null},G=R.epoch,X=R.provider,K=R.model;if(!G&&o){let U=await o(E,F);U?.trackId&&U?.boxId!=null&&(G={trackId:U.trackId,boxId:U.boxId,epoch:U.epoch??0},X=X??U.provider??null,K=K??U.model??null)}if(!G){B+=q;continue}let he=Number.isFinite(z)?z:F?.ts??a.ts,ce=Math.max(0,(a.ts-he)/$1),re=qs(G),Ae=re===u?"self":M1(ce,r);P.push({sha:E,key:re,trackId:G.trackId,boxId:G.boxId,epoch:G.epoch,provider:X??null,model:K??null,lines:q,ageDays:Math.round(ce*100)/100,class:Ae,decay:Ae==="rework"?1:Ae==="drift"?Math.round(N1(ce,{window:r})*1e3)/1e3:0})}P.sort((E,q)=>q.lines-E.lines);let L=yu({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=Om({heuristic:L,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:p,deleted:h,excludedDeleted:w,cosmeticDeleted:x,docsDeleted:g,docsAdded:b,configDeleted:v,codeDelta:p+h+x>0,origins:P,unattributedLines:B,ghost:h===0&&p>0,heuristic:L,intent:A}}function tS(e,{minLines:t=Rm}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Gs.has(n)?$i(e)>=t:!1}function $i(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Em(e){let t=e.genUsd;return{...e,genUsd:on(t),reworkChargedUsd:on(e.reworkChargedUsd),reworkPendingUsd:on(e.reworkPendingUsd),trueUsd:on(t+e.reworkChargedUsd),multiplier:t>0?on((t+e.reworkChargedUsd)/t):null}}function nS(e,{window:t=Br,boxMeta:n={}}={}){let r=e||[],o=r.filter(A=>A.type==="cost.recorded"),s=r.filter(A=>A.type==="code.committed"),a=r.filter(A=>A.type==="code.rework"),i=A=>Array.isArray(A?.shas)&&A.shas.length?A.shas:A?.sha?[A.sha]:[],c=new Map,l=new Set;for(let A of r)if(A.type==="rework.cause.offered")for(let E of i(A.payload))l.add(E);else if(A.type==="rework.cause.answered"&&A.payload?.cause)for(let E of i(A.payload))c.set(E,A.payload.cause);else if(A.type==="rework.cause.retracted")for(let E of i(A.payload))c.delete(E);let u={},f=(A,E,q)=>qs({trackId:A,boxId:E,epoch:q??0}),d=(A,E={})=>{if(!u[A]){let z=hu(A)||{};u[A]={key:A,...z,provider:null,model:null,...Am()}}let q=u[A];return E.provider&&!q.provider&&(q.provider=E.provider),E.model&&!q.model&&(q.model=E.model),q};for(let A of o){if(!A.boxId)continue;let E=f(A.trackId,A.boxId,A.epoch),{usd:q,model:z}=Ut(A),F=d(E,{provider:A.provider,model:z!=="unknown"?z:null});F.genUsd+=q}let p=new Map;for(let A of s){let E=`${A.trackId}/${A.boxId}`;(p.get(E)??p.set(E,[]).get(E)).push({...A,ts:A.payload?.ts??A.ts})}for(let A of p.values())A.sort((E,q)=>E.ts-q.ts);let h=(A,E,q,z)=>{let F=p.get(`${A}/${E}`)||[],R=-1/0;for(let K of F)K.ts<z&&(R=K.ts);let G=0,X=0;for(let K of o)K.trackId!==A||K.boxId!==E||(K.epoch??0)===(q??0)&&(K.ts<=R||K.ts>z||(G+=Ut(K).usd,X++));return{usd:G,turns:X,from:Number.isFinite(R)?R:null}},w={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},g={A:0,B:0,C:0,E:0,none:0},b={fix:0,"likely-fix":0,planned:0,unknown:0},v=0,x=[],I=[],P={},B={},j=new Set;for(let A of a){let E=A.payload||{};if(!E.sha||j.has(E.sha))continue;if(j.add(E.sha),E.codeDelta===!1){v++;continue}let q=hu(E.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:z,turns:F}=h(q.trackId,q.boxId,q.epoch,E.ts??A.ts),R=O1.includes(E.intent?.intent)?E.intent.intent:"unknown";b[R]+=1;let G=null;c.has(E.sha)?G={cause:c.get(E.sha),level:"B",basis:"operator"}:(E.heuristic?.level&&E.heuristic.level!=="C"||E.heuristic)&&(G=E.heuristic);let X=G?.level??"none";g[X]=(g[X]||0)+1;let K=R1.has(X),he=Number(E.deleted)||0,ce=Array.isArray(E.origins)?E.origins:[],re=ce.filter(U=>U.class==="rework").reduce((U,de)=>U+de.lines,0),Ae={sha:E.sha,subject:E.subject??"",ts:E.ts??A.ts,trackId:A.trackId,fixKey:E.fixKey??null,fixProvider:E.fixProvider??A.provider??null,fixUsd:on(z),fixTurns:F,linesReworked:re,unattributedLines:Number(E.unattributedLines)||0,ghost:E.ghost===!0,intent:{intent:R,basis:E.intent?.basis??null},verdict:G?{cause:G.cause,level:X,basis:G.basis??null}:null,charged:on(0),origins:[]};if(R==="planned"||G?.cause===wu){w.planned+=z;for(let U of ce)U.class==="rework"&&Ae.origins.push({key:U.key,provider:U.provider??null,model:U.model??null,lines:U.lines,ageDays:U.ageDays,class:U.class,usd:0})}else if(E.ghost||he===0)w.ghost+=z;else{let U=0;for(let ae of ce){let Y=ae.lines/he,Re=z*Y;if(U+=Re,ae.class==="self"){w.self+=Re;continue}let pe=ae.class==="rework"?1:Number(ae.decay)||0,Ie=Re*pe;if(w.decayed+=Re-Ie,Ie<=0)continue;let _=d(ae.key,{provider:ae.provider,model:ae.model});_.fixes+=1,_.linesReworked+=ae.class==="rework"?ae.lines:0;let N=G?.cause&&Ii.includes(G.cause)?G.cause:"unclassified";_.causes[N]+=1,K?(_.reworkChargedUsd+=Ie,w.charged+=Ie,Ae.charged=on(Ae.charged+Ie)):(_.reworkPendingUsd+=Ie,w.pending+=Ie),Ae.origins.push({key:ae.key,provider:_.provider,model:_.model,lines:ae.lines,ageDays:ae.ageDays,class:ae.class,usd:on(Ie)})}let de=Math.max(0,z-U);w.unattributed+=de}x.push(Ae),!c.has(E.sha)&&l.has(E.sha)&&I.push(E.sha)}for(let A of Object.values(u)){let E=A.provider||"unknown",q=A.model||"unknown",z=F=>{F.genUsd+=A.genUsd,F.reworkChargedUsd+=A.reworkChargedUsd,F.reworkPendingUsd+=A.reworkPendingUsd,F.linesReworked+=A.linesReworked,F.fixes+=A.fixes;for(let R of Object.keys(F.causes))F.causes[R]+=A.causes[R]};z(P[E]||=Am()),z(B[q]||=Am())}x.sort((A,E)=>E.ts-A.ts);let L=Object.values(u).map(A=>{let E=n[`${A.trackId}/${A.boxId}`]||{};return Em({...A,trackName:E.trackName??null,label:E.label??null})}).sort((A,E)=>E.reworkChargedUsd+E.reworkPendingUsd-(A.reworkChargedUsd+A.reworkPendingUsd)||E.genUsd-A.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${Qv} similar are counted, not blamed); fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause; planned work (follow-up, blueprint item, feat:/docs: prefix, non-fix prompt) is generation cost`,intents:b,noCodeDelta:v,byEpoch:Object.fromEntries(L.map(A=>[A.key,A])),epochs:L,byProvider:Object.fromEntries(Object.entries(P).map(([A,E])=>[A,Em(E)])),byModel:Object.fromEntries(Object.entries(B).map(([A,E])=>[A,Em(E)])),buckets:Object.fromEntries(Object.entries(w).map(([A,E])=>[A,on(E)])),fixUsdTotal:on(Object.values(w).reduce((A,E)=>A+E,0)),fixes:x,verdicts:g,pendingTaps:I,commits:s.length}}var _1,I1,_m,Jv,Xv,Zv,$1,Br,$m,Rm,Ii,wu,R1,O1,Gs,Qv,C1,P1,Vv,D1,B1,j1,L1,F1,zv,U1,H1,W1,q1,Im,mu,on,Am,bu=H(()=>{Er();_1=E1(A1),I1=64*1024*1024,_m=(e,t)=>_1("git",["-C",e,...t],{maxBuffer:I1,timeout:6e4}),Jv="Bostrat-Epoch",Xv="Bostrat-Model",Zv="Bostrat-Fix-Cause",$1=24*36e5,Br=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,$m=90,Rm=3,Ii=Object.freeze(["defect","design","requirements","style"]),wu="not-a-fix",R1=new Set(["A","B","E"]),O1=Object.freeze(["fix","likely-fix","planned","unknown"]),Gs=new Set(["fix","likely-fix"]),Qv=.8;C1={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};P1=/^revert\b|^revert "/i,Vv=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,D1=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,B1=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;j1=/^([a-z]+)(\([^)]*\))?!?:\s/i,L1=new Set(["fix","hotfix","bugfix","revert"]),F1=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),zv=/\bBP-[A-Z]*\d+\b/i,U1=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);H1=[/(^|\/)\.bostrat\//,/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock|Gemfile\.lock|go\.sum|composer\.lock)$/,/\.min\.(js|css)$/,/\.map$/,/\.snap$/,/(^|\/)(node_modules|dist|build|vendor|\.next|target)\//],W1=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],q1=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Im=e=>String(e??"").replace(/\s+/g," ").trim(),mu=400;on=e=>Math.round(e*1e4)/1e4,Am=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as X1}from"node:child_process";import{promisify as Z1}from"node:util";function rS(e,t=Date.now()){return mf(e,t)??0}function eB(e,t,n){for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="cost.recorded"||o.boxId!==t||n!=null&&o.epoch!=null&&o.epoch!==n)continue;let s=o.payload?.model;if(s&&s!=="unknown")return s}return null}async function oS(e,t,{now:n=Date.now()}={}){let r=rS(t,n),o=await W(e);return gu({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:eB(o,t.name,r)})}function tB(e,t){let n=null;for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="turn.user"||o.boxId!==t||o.actor!=="user")continue;let s=o.payload||{};if(s.note?.kind!=="task"){if(o.source==="orchestrator"&&typeof s.text=="string"&&s.text.trim())return{text:s.text,kind:s.kind??null};!n&&typeof s.promptHead=="string"&&s.promptHead.trim()&&(n={text:s.promptHead,kind:s.kind??null})}}return n}function nB(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=tB(n,t.name),c=i?Ln(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function rB(e,t){try{let{stdout:n}=await Cm(e,["show","--shortstat","--format=",t]),r=/(\d+) files? changed/.exec(n),o=/(\d+) insertions?\(\+\)/.exec(n),s=/(\d+) deletions?\(-\)/.exec(n);return{files:r?Number(r[1]):0,insertions:o?Number(o[1]):0,deletions:s?Number(s[1]):0}}catch{return{files:0,insertions:0,deletions:0}}}async function oB(e){try{let{stdout:t}=await Cm(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function sS(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await W(e);if(a.some(h=>h.type==="code.committed"&&h.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:h}=await Cm(r,["show","-s","--format=%ct",n]),w=Number(h.trim())*1e3;Number.isFinite(w)&&w>0&&(i=w)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=rS(t,i),l=await rB(r,n),u=await oB(r),f=await $(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:u,...l,ts:i}}),d=null,p=!1;try{let h=await eS(r,n,{fixKey:qs({trackId:e.id,boxId:t.name,epoch:c}),window:Br,context:{...nB(e,t,a,c),ref:u,provider:t.agent??null}});if(h&&(d=await $(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:f.id,payload:h}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&tS(h))){let g=$i(h),b=h.origins.filter(v=>v.class==="rework").slice(0,3);await $(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:{sha:n,subject:h.subject,linesReworked:g,origins:b.map(v=>({key:v.key,provider:v.provider,model:v.model,lines:v.lines,ageDays:v.ageDays})),heuristic:h.heuristic,intent:h.intent,ts:i}}),p=!0}}catch(h){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${h?.message||h}`)}return{recorded:!0,event:f,rework:d,offered:p}}function aS(e,t){let n=0;for(let r of e)r.payload?.sha===t&&(r.type==="rework.cause.answered"?n++:r.type==="rework.cause.retracted"&&n--);return n>0}async function iS(e,t,n){if(!Ii.includes(n)&&n!==wu)throw new Error(`bad cause "${n}" (${Ii.join("|")}|${wu})`);let r=await W(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(aS(r,t))throw new Error("already answered");return $(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,...Array.isArray(o.payload?.shas)?{shas:o.payload.shas}:{},cause:n,level:"B",ts:Date.now()}})}async function cS(e,t){let n=await W(e);if(!aS(n,t))throw new Error("no standing answer to retract");let r=[...n].reverse().find(o=>o.type==="rework.cause.answered"&&o.payload?.sha===t);return $(e,{type:"rework.cause.retracted",actor:"user",source:"orchestrator",boxId:r?.boxId??null,causationId:r?.id,payload:{sha:t,...Array.isArray(r?.payload?.shas)?{shas:r.payload.shas}:{},ts:Date.now()}})}async function ku(e,{repo:t=null,number:n,title:r="",branch:o=null,url:s=null}={},{log:a=console,now:i=Date.now()}={}){try{if(!e?.id||n==null)return{offered:!1,reason:"incomplete"};let c=await W(e);if(c.some(v=>v.type==="rework.cause.offered"&&v.payload?.pr?.number===n&&(t==null||v.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let v of c)if(v.type==="rework.cause.offered")for(let x of Array.isArray(v.payload?.shas)?v.payload.shas:[v.payload?.sha])x&&l.add(x);let u=new Map;for(let v of c)v.type==="code.committed"&&v.payload?.sha&&u.set(v.payload.sha,v.payload.ref??null);let f=Om({heuristic:yu({subject:r}),subject:r,ref:o}),d=[];for(let v of c){if(v.type!=="code.rework")continue;let x=v.payload||{};if(!x.sha||l.has(x.sha)||x.codeDelta===!1||o&&u.has(x.sha)&&u.get(x.sha)!==o)continue;let I=x.intent?.intent??"unknown";(Gs.has(I)||I==="unknown"&&Gs.has(f.intent))&&$i(x)!==0&&d.push(x)}let p=d.reduce((v,x)=>v+$i(x),0);if(p<Rm)return{offered:!1,reason:"below-threshold"};let h=new Map;for(let v of d)for(let x of v.origins??[]){if(x.class!=="rework")continue;let I=h.get(x.key)??{key:x.key,provider:x.provider??null,model:x.model??null,lines:0,ageDays:x.ageDays};I.lines+=x.lines,I.ageDays=Math.min(I.ageDays,x.ageDays),h.set(x.key,I)}let w=d.map(v=>v.sha),g=d.map(v=>v.heuristic).find(v=>v&&v.level!=="C")??yu({subject:r})??d.map(v=>v.heuristic).find(Boolean)??null,b=Gs.has(f.intent)?f:d.find(v=>Gs.has(v.intent?.intent))?.intent??f;return await $(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:null,payload:{sha:w[w.length-1],shas:w,pr:{repo:t,number:n,title:String(r||"").slice(0,200),url:s,branch:o},subject:String(r||"").slice(0,200)||d[d.length-1].subject,linesReworked:p,origins:[...h.values()].sort((v,x)=>x.lines-v.lines).slice(0,3),heuristic:g,intent:b,ts:i}}),{offered:!0,shas:w}}catch(c){return a.warn?.(`[rework] ${e?.id}: PR-level offer for #${n} failed: ${c?.message||c}`),{offered:!1,reason:"error"}}}var Q1,Cm,xu=H(()=>{ge();_l();bu();Q1=Z1(X1),Cm=(e,t)=>Q1("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as sB}from"node:child_process";import{promisify as aB}from"node:util";async function Mm(e,t,{midTurn:n=_o,pollMs:r=iB,capMs:o=cB}={}){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 mS(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){lS.lastIndex=n;let o=lS.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(`
|
|
192
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+hB(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 Nm(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function wS(e){let t=mS(e),n=t.match(fB);if(!n)return null;let r=Nm(t,n.index);return mB.test(r)?null:{repo:r.match(hS)?.[1]??null,head:r.match(wB)?.[1]??null}}function gS(e){let t=mS(e),n=t.match(pB);if(!n)return null;let r=Nm(t,n.index),o=Nm(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("-")){yB.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(hS)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function kB(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Gn("observer");let{stdout:c}=await uS("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",bB],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Nr(l)&&Pr(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-lB&&!n(c)),s=await pn({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 xB(e,t){if(t)return[t];let n=[];for(let r of yS(e)){let o=await Ee(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Pn().catch(()=>[])){let o=await Ee(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function yS(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 vB(e,t,n){let r=await Ws(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)||gB(c.headRefName,e.id),i=[];for(let c of await xB(e,t?.repo??null)){let l;try{l=await kB(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await ot(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 bS(){for(let e of[...Pm])e()}function kS(e,t,{delays:n=dS,turnEnd:r}={}){let o=wS(t.command);if(!o)return null;let s=!!gS(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(jr.has(a))return null;jr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>{let g=()=>{clearTimeout(b),Pm.delete(g),w()},b=setTimeout(g,l);Pm.add(g)});let u=await vB(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:h}=await Rt({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!h&&!(p?.trackId===e.id&&await Hn(f,d.number))?null:(await Mm(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{jr.delete(a)}})();return c.catch(()=>jr.delete(a)),c}function xS(e,t,{delays:n=dS,turnEnd:r,openedWait:o}={}){let s=gS(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(jr.has(a))return null;jr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let h=yS(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let g=await Ee(w).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=(await Ws(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(h,w)=>{let g=await ot(c,h.number);return g&&g.trackId!==e.id?null:(g||await Rt({repo:c,number:h.number,trackId:e.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await yi(c,h.number),w&&((await W(e).catch(()=>[])).some(x=>x.type==="pr.state.changed"&&(x.payload?.change==="auto-armed"||x.payload?.change==="merged")&&x.payload?.number===h.number)||(await Mm(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:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},p=null;for(let h of n){await new Promise(B=>setTimeout(B,h));let w=[];for(let B of f)try{Gn("observer");let{stdout:j}=await uS("gh",["pr","view",String(l),"--repo",B,"--json",SB],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await pn({slug:B})}}),L=JSON.parse(j||"null");L?.number&&w.push({slug:B,pr:L})}catch(j){Nr(j)&&Pr(j)}let g=w.find(B=>B.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!g)continue;c=g.slug;let b=g.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(p=b,s.auto)return d(b,!0);continue}let v=await ot(c,b.number);if(v&&v.trackId!==e.id)return null;v||await Rt({repo:c,number:b.number,trackId:e.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await Mm(t.box,t.agent,r);let x=await W(e).catch(()=>[]);if(x.some(B=>B.type==="pr.state.changed"&&B.payload?.change==="merged"&&B.payload?.number===b.number))return null;let I=B=>B.some(j=>j.type==="pr.state.changed"&&j.payload?.change==="opened"&&j.payload?.number===b.number),P=0;if(!I(x)){if(wS(t.command)&&await Hn(c,b.number)){let B=Date.now();P=B+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:B,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await ot(c,b.number))?.announced){let B=o?.capMs??uB,j=o?.pollMs??dB,L=Date.now()+B;for(;Date.now()<L&&(await new Promise(A=>setTimeout(A,j)),!I(await W(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(),P),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await ku(e,{repo:c,number:b.number,title:b.title,branch:b.headRefName,url:b.url}).catch(()=>{}),await zn(c,b.number).catch(()=>{}),await Vn(c,b.number).catch(()=>{}),await Cr(c,b.number,"merged"),b}return p?d(p,!1):null}finally{jr.delete(a)}})();return i.catch(()=>jr.delete(a)),i}var uS,dS,iB,cB,lB,uB,dB,fS,pS,fB,pB,mB,lS,hB,hS,wB,gB,yB,bB,jr,Pm,SB,Dm=H(()=>{ge();ur();ct();xi();Wn();Yn();_i();Io();xu();uS=aB(sB),dS=[4e3,15e3,45e3],iB=2500,cB=5*6e4;lB=15e3,uB=9e4,dB=2e3,fS=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,pS=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,fB=new RegExp(`${fS}${pS}create\\b`),pB=new RegExp(`${fS}${pS}merge\\b`),mB=/\s--web\b/,lS=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,hB=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");hS=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,wB=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,gB=Gv;yB=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);bB="number,url,title,headRefName,createdAt";jr=new Set,Pm=new Set;SB="number,url,title,headRefName,state"});import TB from"node:fs/promises";import jm from"node:path";import AB from"node:os";function Bm(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function vu(e){let t=EB.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Bm(n,"from"),s=o?o.replace(/^uds:/,""):null,a=vS(s);return{socket:s,pid:a,name:Bm(n,"from-name"),mode:Bm(n,"from-mode"),text:r.trim()}}function vS(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function _B(){return process.env.CLAUDE_CONFIG_DIR?jm.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):jm.join(AB.homedir(),".claude","sessions")}async function IB(e){let t=vS(e);if(!t)return null;try{let n=await TB.readFile(jm.join(_B(),`${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 SS(e,t){let n=await IB(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 EB,Lm=H(()=>{EB=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function Fm(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Um({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Fm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),TS);try{let a=await AS(`${Oe(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 ES(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(!Fm(s))return{ok:!1,source:"unconfigured"};let i=Su.get(e.id)||0;if(a()-i<$B)return{ok:!1,source:"debounced"};Su.set(e.id,a());let c=await Ri({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return Su.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 Um({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||Su.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function Ri({env:e=process.env}={}){if(!Fm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),TS);try{let r=await AS(`${Oe(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 TS,AS,$B,Su,Hm=H(()=>{ct();TS=1e4,AS=(...e)=>fetch(...e);$B=10*6e4,Su=new Map});function _S(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}async function IS(e,t,n=Wm){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await OB(`${Oe(t)}${e}`,{headers:CB(t),signal:r.signal})}finally{clearTimeout(o)}}async function Oi({env:e=process.env,force:t=!1,timeoutMs:n=Wm}={}){if(!_S(e))return{followups:[],source:"unconfigured"};if(!t&&$o&&Date.now()-$o.at<RB)return{followups:$o.followups,source:"cache"};try{let r=await IS("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return $o={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return $o?{followups:$o.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function $S(e,{env:t=process.env,timeoutMs:n=Wm}={}){if(!e)return{followup:null,source:"unconfigured"};if(!_S(t))return{followup:null,source:"unconfigured"};try{let r=await IS(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=$o?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var RB,Wm,$o,OB,CB,RS=H(()=>{ct();RB=6e4,Wm=5e3,$o=null,OB=(...e)=>fetch(...e);CB=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import{randomUUID as Vs,createHash as MB}from"node:crypto";function NS(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function jB(e){for(let[t,n]of BB)if(t.test(String(e||"")))return`a standing prompt must not embed this Track's specifics (${n}) \u2014 the card supplies them at fire time; arm a one-shot with \`offer\` instead`;return null}function PS(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Ks(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>OS)throw new Error(`label must be \u2264${OS} characters`);let o=Ks(n.trigger)||(n.ship?"now":""),s=t?PB:MS.filter(u=>u!=="track.opened");if(!s.includes(o))throw new Error(`trigger must be one of ${s.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let a=n.ship&&o==="now"?FB(n.ship):null,i=Ks(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>CS)throw new Error(`prompt must be \u2264${CS} bytes`);if(t){let u=jB(i);if(u)throw new Error(u)}let c=Ks(n.why).slice(0,NB)||null,l=t?LB(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function LB(e){if(e==null||e===""||e==="*")return null;let t=String(e).trim();if(!/^[\w.-]+\/[\w.-]+$/.test(t))throw new Error(`repo must be a forge slug like owner/name, got "${e}"`);return t}function FB(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):qm.slice();for(let i of n)if(!qm.includes(i))throw new Error(`ship.actions may only contain ${qm.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=Ks(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>Ks(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Tu(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}async function DS(e,t,{pull:n=Oi,ship:r={}}={}){if(Vm(e))throw new Error("follow-ups don't fire on a loop's run Track \u2014 report the outcome with `bostrat-loop report` instead");let o=PS(t);if(o.ship)return{kind:"ship",...await WB(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null};if(o.trigger==="now"){let g=Vs(),b=Vs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:g,trigger:"now",label:o.label,prompt:o.prompt,why:o.why}}),await $(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:b,trigger:"now",repo:e.workspace?.repo||null,context:{},rows:[{armId:g,label:o.label,prompt:o.prompt,promptHash:Gm(o.prompt),origin:"agent",why:o.why}]}}),{kind:"now",armId:g,offerId:b,...o}}let c=e.workspace?.repo||null,{followups:l}=await n().catch(()=>({followups:[]})),u=NS(o.label),f=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(f)return{kind:"covered",covered:{id:f.id,label:f.label},...o};let d=await W(s),p=Tu(d,{trigger:o.trigger}),h=p.length?p[p.length-1].armId:null,w=Vs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:w,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...h?{replaces:h}:{}}}),{kind:"armed",armId:w,replaces:h,...o}}async function BS(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await W(n);if(!Tu(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await $(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function jS(e,t,{pull:n=Oi}={}){if(Vm(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=PS(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await W(s)).reduce((g,b)=>b.type==="followup.create.offered"?b.payload?.proposalId??g:b.type==="followup.create.answered"&&b.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=NS(r.label),f=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),d=f.find(g=>g.status==="deleted"),p=f.find(g=>g.status!=="deleted");if(p)throw new Error(`"${p.label}" (${Km[p.trigger]||p.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(d){let g=d.deletedAt?new Date(d.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${d.label}" on ${g} \u2014 do not re-propose it`)}let h=Vs(),w=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await $(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:w?.agent??null,payload:{ts:Date.now(),proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function LS(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await W(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await $(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Ci(e,t,n={},{pull:r=Oi}={}){if(!e||Vm(e))return null;let o={id:e.id,cwd:e.cwd},s=await W(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null,i=Tu(s,{trigger:t}).map(d=>({armId:d.armId,label:d.label,prompt:d.prompt,promptHash:Gm(d.prompt),origin:"agent",why:d.why??null})),c=[];if(t!=="now"){let{followups:d}=await r().catch(()=>({followups:[]}));c=d.filter(p=>p.status==="active"&&p.mode!=="off"&&p.trigger===t&&(p.repo==null||p.repo===a)).sort((p,h)=>{let w=g=>g.stats?.offered?(g.stats.accepted||0)/g.stats.offered:0;return w(h)-w(p)||(p.origin==="operator"?-1:0)-(h.origin==="operator"?-1:0)||(h.createdAt||0)-(p.createdAt||0)}).map(p=>({followupId:p.id,label:p.label,promptPreview:UB(p.prompt),promptHash:Gm(p.prompt),origin:p.origin,repo:p.repo??null}))}let l=[...i,...c].slice(0,DB);if(!l.length)return null;let u=Vs(),f={ts:Date.now(),offerId:u,trigger:t,repo:a,context:{...n,repo:a},rows:l};return await $(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:f}),f}function HB(e){let t=e?.context||{},n=Array.isArray(t.prs)&&t.prs.length?t.prs:t.prNumber?[{number:t.prNumber,title:t.prTitle,url:t.prUrl,repo:t.repo}]:[],r=o=>`PR #${o.number}${o.title?` "${o.title}"`:""}${o.url?` (${o.url})`:""}${o.repo?` on ${o.repo}`:""}`;switch(e?.trigger){case"pr.merged":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} merged:`:"Follow-up the operator tapped after this Track's PR merged:";case"pr.opened":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} opened:`:"Follow-up the operator tapped after this Track's PR opened:";case"checks.green":return n.length?`Follow-up the operator tapped after CI went green on ${n.map(r).join(" and ")}:`:"Follow-up the operator tapped after CI went green:";case"ship.done":return"Follow-up the operator tapped after the ship you just ran completed:";default:return"Follow-up the operator tapped:"}}async function FS(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=$S}=s;if(!t)throw new Error("offerId required");let l={id:e.id,cwd:e.cwd},u=await W(l),f=u.find(x=>x.type==="followup.offered"&&x.payload?.offerId===t);if(!f)throw new Error("unknown offer");if(u.some(x=>x.type==="followup.answered"&&x.payload?.offerId===t))throw new Error("offer already answered");let d=f.payload.rows||[],p=x=>$(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...x}});if(!n&&!r)return await p({action:"dismiss",followupIds:d.map(x=>x.followupId).filter(Boolean),armIds:d.map(x=>x.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let h=null,w=null;if(n){if(!d.some(P=>P.followupId===n))throw new Error("that follow-up is not on this card");let{followup:x,source:I}=await c(n);if(I==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!x||x.status!=="active"){await p({action:"removed",followupId:n});let P=new Error("removed in Settings since it was offered");throw P.code="removed",P}h=x.prompt,w=x.label}else{let x=d.find(P=>P.armId===r);if(!x)throw new Error("that arm is not on this card");if(h=u.find(P=>P.type==="followup.armed"&&P.payload?.armId===r)?.payload?.prompt??x.prompt,w=x.label,!h)throw new Error("the arm's prompt is missing from the log")}let g=!1;if(e.archivedAt){if(!i)throw new Error("this Track is archived");await i(e.id,{freshSession:!0}),g=!0}if(!a)throw new Error("sayToLead unavailable");let b=`${HB(f.payload)}
|
|
188
193
|
|
|
189
|
-
${h}`,
|
|
194
|
+
${h}`,v=await a(e.id,b,{kind:"followup-go"});return await p({action:"sent",...n?{followupId:n}:{armId:r},label:w,...o?{promoted:String(o)}:{},...g?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:g,queued:!!v?.queued}}async function WB(e,t,{changedRepos:n=null,probe:r=du,branchOf:o=Dr}={}){let s=t.ship;if(!e.workspace?.branch)throw new Error("this Track has no branch to ship \u2014 a ship offer needs a worktree Track");let a={id:e.id,cwd:e.cwd},i=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,c=e.workspace?.kind==="multi",l;if(c&&n){let g=await n(e).catch(()=>[]),b=s.repos?g.filter(v=>s.repos.includes(v.name)):g;l=b.length?b.map(v=>({name:v.name,slug:v.repo??null})):void 0}let u=(c?null:await o(e.cwd).catch(()=>null))||e.workspace.branch,f=s.rebase==="offered",d=await r(e,{branch:u,rebase:f}).catch(()=>null),p=[...f?["rebase"]:[],...s.actions].filter(g=>Rr.includes(g));if(d&&d.available.length===0)return{skipped:!0,reason:"nothing to ship \u2014 the tree is clean, pushed, and its PR is open",shipState:d};let h=Vs(),w=t.why?`${t.label} \u2014 ${t.why}`:t.label;return await $(a,{type:"pr.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:i?.agent??null,payload:{offerId:h,ts:Date.now(),excerpt:w,via:"cli",branch:u,base:s.base||e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:"lead",available:d?.available,defaults:d?d.defaults.filter(g=>p.includes(g)||g==="rebase"):void 0,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||l,offered:d?p.filter(g=>d.available.includes(g)):p,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:h,branch:u,offered:p,available:d?.available??null,prState:d?.prState??null}}function US(e){let t=!1;for(let n of e)n.type==="pr.offered"&&(n.source==="cli"||n.payload?.via==="cli")?t=!0:(n.type==="turn.user"&&!n.payload?.kind||n.type==="pr.offer.answered")&&(t=!1);return t}function HS(e){let t=null;for(let n of e){let r=n.payload||{};n.type==="pr.offer.answered"?t=r.action&&r.action!=="dismiss"&&r.action!=="pr-exists"?r.offerId:null:n.type==="pr.exists.answered"?t=r.choice&&r.choice!=="dismiss"?r.offerId??t:null:(n.type==="turn.user"&&!r.kind||n.type==="followup.offered"&&r.trigger==="ship.done"&&r.context?.shipOfferId&&r.context.shipOfferId===t)&&(t=null)}return t}async function WS(e,{pull:t=Oi}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await W({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?Tu(s):[]}}var OS,CS,NB,MS,PB,Km,DB,qm,Gm,Ks,BB,Vm,UB,Au=H(()=>{ge();xo();Sm();Io();RS();OS=60,CS=8*1024,NB=200,MS=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],PB=MS.filter(e=>e!=="now"&&e!=="track.opened"),Km={now:"now","pr.merged":"after merge","ship.done":"after ship","pr.opened":"after PR opens","checks.green":"after CI goes green","track.opened":"when a Track opens"},DB=3,qm=["commit","push","pr"];Gm=e=>MB("sha256").update(String(e)).digest("hex").slice(0,16),Ks=e=>typeof e=="string"?e.trim():"",BB=[[/(^|[^\w&])#\d+\b/,"a PR/issue number"],[/https?:\/\/\S+/i,"a URL"],[/\b[0-9a-f]{7,40}\b/i,"a commit SHA"],[/\bv\d+\.\d+\.\d+\b/,"a release tag"]];Vm=e=>String(e?.name||"").startsWith("\u21BB");UB=e=>String(e||"").split(`
|
|
190
195
|
`).slice(0,3).join(`
|
|
191
|
-
`).slice(0,400)});import{randomUUID as
|
|
192
|
-
`)){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 yB(e,t){let n=`${e.id}:${t.name}`;if(!xm.has(n)){let r=await F(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);xm.set(n,o)}return xm.get(n)}async function tS(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await so(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await yB(e,t),o=gB(await Et(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await $(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:mn(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 xm,nS=j(()=>{me();io();Xt();Dn();xm=new Map});import fu from"node:fs";import Sm from"node:path";import bB from"node:os";async function vB({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(xB,{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:rS})});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<rS)break}return r}function SB(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 sS(){try{return JSON.parse(fu.readFileSync(pu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function EB(e){try{fu.mkdirSync(Sm.dirname(pu()),{recursive:!0});let t=pu()+".tmp";fu.writeFileSync(t,JSON.stringify(e)),fu.renameSync(t,pu())}catch{}}async function _B({fetchImpl:e,now:t=Date.now()}={}){if(Pr.rows&&t-Pr.at<TB)return Pr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=sS(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await vB({sinceMs:o,cookie:n,fetchImpl:e});Pr={at:t,rows:s.map(SB).filter(Boolean),error:null}}catch(s){Pr={at:t,rows:null,error:String(s?.message||s)}}return Pr.rows}async function IB(e,t){let n=`${e.id}:${t.name}`;if(!vm.has(n)){let r=await F(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);vm.set(n,o)}return vm.get(n)}async function aS(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!oS())return;let r=await _B({fetchImpl:n});if(!r||!r.length)return;let o=await u0(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await IB(e,t),a=sS(),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=mn(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}})}EB({sinceMs:c,seen:[...i].slice(-AB)})}catch{}}var kB,oS,xB,rS,TB,Pr,pu,AB,Tm,vm,Em=j(()=>{me();fp();Dn();kB=process.env.BOSTRAT_STATE_DIR||Sm.join(process.env.HOME||bB.tmpdir(),".bostrat"),oS=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,xB="https://cursor.com/api/dashboard/get-filtered-usage-events",rS=100;TB=5*6e4,Pr={at:0,rows:null,error:null},pu=()=>Sm.join(kB,"cursor-usage.json");AB=5e3;Tm=()=>({enabled:oS(),lastPollAt:Pr.at||null,error:Pr.error}),vm=new Map});import{execFile as $B}from"node:child_process";import{promisify as RB}from"node:util";function Us({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function mu(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function hu({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?PB[r]:null;return a&&s.push(a),s.push(`${uS}: ${Us({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${dS}: ${String(o).trim()}`),s}function iS(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
193
|
-
`)
|
|
194
|
-
`)){if(r.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1},t.push(n);continue}if(n){if(r.startsWith("--- ")){let o=r.slice(4).trim();n.oldPath=o==="/dev/null"?null:o.replace(/^a\//,"")}else if(r.startsWith("+++ ")){let o=r.slice(4).trim();n.newPath=o==="/dev/null"?null:o.replace(/^b\//,"")}else if(r.startsWith("Binary files"))n.binary=!0;else if(r.startsWith("@@")){let o=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(r);if(!o)continue;let s=Number(o[1]),a=o[2]==null?1:Number(o[2]),i=o[4]==null?1:Number(o[4]);a>0&&n.deleted.push([s,a]),n.added+=i}}}return t}function KB(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
195
|
-
`)){let o=/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(r);if(o){n={sha:o[1],line:Number(o[3]),time:null},t.set(n.line,n);continue}n&&r.startsWith("committer-time ")&&(n.time=Number(r.slice(15))*1e3)}return t}async function lS(e,t){let{stdout:n}=await Im(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
196
|
-
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
197
|
-
`)}}async function mS(e,t,{fixKey:n=null,window:r=Dr,resolveOrigin:o=null,context:s={}}={}){let a=await lS(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=iS(a.message),l=n?mu(n):c.epoch,u=l?Us(l):null,{stdout:f}=await Im(e,["diff","-U0","--no-color","-M","-C",i,t]),d=qB(f),p=0,h=0,w=0,g=new Map;for(let B of d){if(B.binary)continue;let T=B.oldPath||B.newPath||"",_=B.deleted.reduce((I,[,U])=>I+U,0);if(WB(T)){w+=_;continue}if(p+=B.added,h+=_,!B.oldPath||!_)continue;let O;try{let I=["blame","-w","-M","-C","--line-porcelain"];for(let[U,H]of B.deleted)I.push("-L",`${U},${U+H-1}`);I.push(i,"--",B.oldPath),O=KB((await Im(e,I)).stdout)}catch{continue}for(let{sha:I,time:U}of O.values()){let H=g.get(I)??{lines:0,time:U};H.lines+=1,g.set(I,H)}}let k=[],S=0,E=new Map;for(let[B,{lines:T,time:_}]of g){let O=E.get(B);if(!O){try{O=await lS(e,B)}catch{O=null}E.set(B,O)}let I=O?iS(O.message):{epoch:null},U=I.epoch,H=I.provider,ae=I.model;if(!U&&o){let W=await o(B,O);W?.trackId&&W?.boxId!=null&&(U={trackId:W.trackId,boxId:W.boxId,epoch:W.epoch??0},H=H??W.provider??null,ae=ae??W.model??null)}if(!U){S+=T;continue}let X=Number.isFinite(_)?_:O?.ts??a.ts,C=Math.max(0,(a.ts-X)/MB),Q=Us(U),ve=Q===u?"self":DB(C,r);k.push({sha:B,key:Q,trackId:U.trackId,boxId:U.boxId,epoch:U.epoch,provider:H??null,model:ae??null,lines:T,ageDays:Math.round(C*100)/100,class:ve,decay:ve==="rework"?1:ve==="drift"?Math.round(BB(C,{window:r})*1e3)/1e3:0})}k.sort((B,T)=>T.lines-B.lines);let R=UB({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:p,deleted:h,excludedDeleted:w,origins:k,unattributedLines:S,ghost:h===0&&p>0,heuristic:R}}function hS(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function _m(e){let t=e.genUsd;return{...e,genUsd:en(t),reworkChargedUsd:en(e.reworkChargedUsd),reworkPendingUsd:en(e.reworkPendingUsd),trueUsd:en(t+e.reworkChargedUsd),multiplier:t>0?en((t+e.reworkChargedUsd)/t):null}}function wS(e,{window:t=Dr,boxMeta:n={}}={}){let r=e||[],o=r.filter(T=>T.type==="cost.recorded"),s=r.filter(T=>T.type==="code.committed"),a=r.filter(T=>T.type==="code.rework"),i=new Map;for(let T of r)T.type==="rework.cause.answered"&&T.payload?.sha&&T.payload?.cause&&i.set(T.payload.sha,T.payload.cause);let c=new Set;for(let T of r)T.type==="rework.cause.offered"&&T.payload?.sha&&c.add(T.payload.sha);let l={},u=(T,_,O)=>Us({trackId:T,boxId:_,epoch:O??0}),f=(T,_={})=>{if(!l[T]){let I=mu(T)||{};l[T]={key:T,...I,provider:null,model:null,...Am()}}let O=l[T];return _.provider&&!O.provider&&(O.provider=_.provider),_.model&&!O.model&&(O.model=_.model),O};for(let T of o){if(!T.boxId)continue;let _=u(T.trackId,T.boxId,T.epoch),{usd:O,model:I}=Ft(T),U=f(_,{provider:T.provider,model:I!=="unknown"?I:null});U.genUsd+=O}let d=new Map;for(let T of s){let _=`${T.trackId}/${T.boxId}`;(d.get(_)??d.set(_,[]).get(_)).push(T)}for(let T of d.values())T.sort((_,O)=>_.ts-O.ts);let p=(T,_,O,I)=>{let U=d.get(`${T}/${_}`)||[],H=-1/0;for(let C of U)C.ts<I&&(H=C.ts);let ae=0,X=0;for(let C of o)C.trackId!==T||C.boxId!==_||(C.epoch??0)===(O??0)&&(C.ts<=H||C.ts>I||(ae+=Ft(C).usd,X++));return{usd:ae,turns:X,from:Number.isFinite(H)?H:null}},h={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0},w={A:0,B:0,C:0,E:0,none:0},g=[],k=[],S={},E={},R=new Set;for(let T of a){let _=T.payload||{};if(!_.sha||R.has(_.sha))continue;R.add(_.sha);let O=mu(_.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:I,turns:U}=p(O.trackId,O.boxId,O.epoch,_.ts??T.ts),H=null;i.has(_.sha)?H={cause:i.get(_.sha),level:"B",basis:"operator"}:(_.heuristic?.level&&_.heuristic.level!=="C"||_.heuristic)&&(H=_.heuristic);let ae=H?.level??"none";w[ae]=(w[ae]||0)+1;let X=NB.has(ae),C=Number(_.deleted)||0,Q=Array.isArray(_.origins)?_.origins:[],ve=Q.filter(be=>be.class==="rework").reduce((be,se)=>be+se.lines,0),W={sha:_.sha,subject:_.subject??"",ts:_.ts??T.ts,trackId:T.trackId,fixKey:_.fixKey??null,fixProvider:_.fixProvider??T.provider??null,fixUsd:en(I),fixTurns:U,linesReworked:ve,unattributedLines:Number(_.unattributedLines)||0,ghost:_.ghost===!0,verdict:H?{cause:H.cause,level:ae,basis:H.basis??null}:null,charged:en(0),origins:[]};if(_.ghost||C===0)h.ghost+=I;else{let be=0;for(let pe of Q){let ie=pe.lines/C,L=I*ie;if(be+=L,pe.class==="self"){h.self+=L;continue}let ce=pe.class==="rework"?1:Number(pe.decay)||0,ue=L*ce;if(h.decayed+=L-ue,ue<=0)continue;let Ae=f(pe.key,{provider:pe.provider,model:pe.model});Ae.fixes+=1,Ae.linesReworked+=pe.class==="rework"?pe.lines:0;let _e=H?.cause&&$i.includes(H.cause)?H.cause:"unclassified";Ae.causes[_e]+=1,X?(Ae.reworkChargedUsd+=ue,h.charged+=ue,W.charged=en(W.charged+ue)):(Ae.reworkPendingUsd+=ue,h.pending+=ue),W.origins.push({key:pe.key,provider:Ae.provider,model:Ae.model,lines:pe.lines,ageDays:pe.ageDays,class:pe.class,usd:en(ue)})}let se=Math.max(0,I-be);h.unattributed+=se}g.push(W),!i.has(_.sha)&&c.has(_.sha)&&k.push(_.sha)}for(let T of Object.values(l)){let _=T.provider||"unknown",O=T.model||"unknown",I=U=>{U.genUsd+=T.genUsd,U.reworkChargedUsd+=T.reworkChargedUsd,U.reworkPendingUsd+=T.reworkPendingUsd,U.linesReworked+=T.linesReworked,U.fixes+=T.fixes;for(let H of Object.keys(U.causes))U.causes[H]+=T.causes[H]};I(S[_]||=Am()),I(E[O]||=Am())}g.sort((T,_)=>_.ts-T.ts);let B=Object.values(l).map(T=>{let _=n[`${T.trackId}/${T.boxId}`]||{};return _m({...T,trackName:_.trackName??null,label:_.label??null})}).sort((T,_)=>_.reworkChargedUsd+_.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||_.genUsd-T.genUsd);return{window:t,basis:`rework = lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old; fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause`,byEpoch:Object.fromEntries(B.map(T=>[T.key,T])),epochs:B,byProvider:Object.fromEntries(Object.entries(S).map(([T,_])=>[T,_m(_)])),byModel:Object.fromEntries(Object.entries(E).map(([T,_])=>[T,_m(_)])),buckets:Object.fromEntries(Object.entries(h).map(([T,_])=>[T,en(_)])),fixUsdTotal:en(Object.values(h).reduce((T,_)=>T+_,0)),fixes:g,verdicts:w,pendingTaps:k,commits:s.length}}var OB,CB,Im,uS,dS,fS,MB,Dr,$m,pS,$i,NB,PB,jB,cS,LB,FB,HB,en,Am,wu=j(()=>{vr();OB=RB($B),CB=64*1024*1024,Im=(e,t)=>OB("git",["-C",e,...t],{maxBuffer:CB,timeout:6e4}),uS="Bostrat-Epoch",dS="Bostrat-Model",fS="Bostrat-Fix-Cause",MB=24*36e5,Dr=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,$m=90,pS=3,$i=Object.freeze(["defect","design","requirements","style"]),NB=new Set(["A","B","E"]);PB={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};jB=/^revert\b|^revert "/i,cS=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,LB=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,FB=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;HB=[/(^|\/)\.bostrat\//,/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock|Gemfile\.lock|go\.sum|composer\.lock)$/,/\.min\.(js|css)$/,/\.map$/,/\.snap$/,/(^|\/)(node_modules|dist|build|vendor|\.next|target)\//];en=e=>Math.round(e*1e4)/1e4,Am=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import Ci from"node:fs/promises";import At from"node:path";import GB from"node:os";import VB from"node:net";import{randomUUID as zB}from"node:crypto";import{execFile as YB}from"node:child_process";import{promisify as JB}from"node:util";function r1(e){for(let t of Dm(e))if(!vS.test(t))return t;return null}function SS(e){let t=Dm(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function o1(e,t){let n=SS(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 a1(e){let t=!1,n=!1;for(let r of Dm(e)){if(!t){if(vS.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Rm(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Rm(r))return`serves the filesystem root ("${r}")`;continue}if(Rm(r))return`serves the filesystem root ("${r}")`;if(s1(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function c1(e,t){if(At.resolve(e)===At.resolve(t))return!0;let[n,r]=await Promise.all([gu(e),gu(t)]);return n===r}async function l1(e){let n=(await Ci.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 TS(At.join(e,o.name,Mm))?`./${o.name}`:null))).filter(Boolean)}async function u1(e,t){let[n,r]=await Promise.all([gu(e),gu(t)]),o=At.relative(n,r);if(o&&(o.startsWith("..")||At.isAbsolute(o)))throw Ri(`serve directory escapes the Track workspace: ${t}`)}async function d1(e,t){let n=a1(t);if(n)throw Ri(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await i1(e))throw Ri(`serve directory does not exist: ${e}`);let r=r1(t);if(!r)return;if(!r.includes("/")&&!await qy(r,{loginShell:!0,failOpen:!0}))throw Ri(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(e1.test(t)||t1.has(r)||!QB.has(r)||!SS(t).includes("run")||n1.test(t))return;let o=o1(e,t);if(await TS(At.join(o,Mm)))return;let s=await l1(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Ri(`no ${Mm} in ${o}${a}`)}async function zn(){return yn||(Om||=(async()=>{try{yn=JSON.parse(await Ci.readFile(xS,"utf8"))}catch{yn={views:[],adoptWatermarks:{}}}finally{Om=null}return yn.views||=[],yn.adoptWatermarks||={},yn})(),Om)}async function kn(){await Tt(xS,yn)}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 jm(e){return Oi.get(e)||null}function AS(e,{name:t,box:n}={}){let r=Oi.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 Oi.set(e,o),o}function Nm(e,t){let n=Oi.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function yu(e,{error:t=null}={}){let n=Oi.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function _S(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 Pm(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function f1(e,t){let n=Pm(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!bS.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!bS.has(s))return s}}function Mi(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=VB.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 Lm(e){if(process.platform!=="darwin"){await bn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await bn("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 IS(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function p1(e,t){let n=new Set(e.views.map(o=>o.port)),r=IS();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=gS;o<=yS;o++)if(!(n.has(o)||r.has(o))&&!await Mi(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${gS}-${yS}`)}async function m1(e){try{let{stdout:t}=await bn(K,V("list-sessions","-F","#{session_name}"));return t.split(`
|
|
198
|
-
`).some(n=>n.trim()===Hs(e))}catch{return!1}}async function Ni(e){try{await bn(K,V("kill-session","-t",Hs(e)))}catch{}}async function Fm(e){if(!await Ha())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Hs(e.slug),n=e.serveCwd??e.cwd,r=At.dirname(process.execPath);await bn(K,V("new-session","-d","-s",t,"-c",n)),await bn(K,V("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await bn(K,V("send-keys","-t",t,"Enter")),await bn(K,V("send-keys","-t",t,"-l",e.cmd)),await bn(K,V("send-keys","-t",t,"Enter"))}async function kS(e,t=12){try{let{stdout:n}=await bn(K,V("capture-pane","-t",Hs(e),"-p"));return String(n||"").trimEnd().split(`
|
|
196
|
+
`).slice(0,400)});import{randomUUID as qB}from"node:crypto";function Mi(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function qS(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function GS(e){return`job|${e.box}|id:${e.id}`}async function Ym(e){return zm.has(e.id)?zm.get(e.id):(Eu.has(e.id)||Eu.set(e.id,W(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(Mi({role:o,sourceId:r.payload.sourceId})),n.keys.add(Mi({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(Mi({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Mi({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(qS({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(GS({id:r.payload.id,box:r.boxId}));return zm.set(e.id,n),n}).finally(()=>Eu.delete(e.id))),Eu.get(e.id))}async function KS(e,t){let n=new Set;try{let r=(await Ym(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=Mi(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?vu(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 SS(a,Ni),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=Kb(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}}),ES(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=Yx(o.text);if(c&&US(await W(e).catch(()=>[])))continue;if(c){let l=Zx(c),u;if(e.workspace?.kind==="multi"){let h=await Ro(e).catch(()=>[]);u=h.length?h.map(w=>({name:w.name,slug:w.repo??null})):void 0}let f=(e.workspace?.kind==="multi"?null:await Dr(e.cwd).catch(()=>null))||e.workspace.branch,d=await du(e,{branch:f,rebase:l}).catch(()=>null),p=d?null:Xx(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:qB(),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 VS(e,t){try{let n=await Ym(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=qS(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}}),kS(e,o),xS(e,o))}}catch{}}async function zS(e,t){try{let n=await Ym(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=GS(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 zm,Eu,YS=H(()=>{ge();xo();Sm();Dm();Io();Lm();Yn();yl();Hm();Au();zm=new Map,Eu=new Map});function GB(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
197
|
+
`)){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 KB(e,t){let n=`${e.id}:${t.name}`;if(!Jm.has(n)){let r=await W(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);Jm.set(n,o)}return Jm.get(n)}async function JS(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await io(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await KB(e,t),o=GB(await At(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:yn(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 Jm,XS=H(()=>{ge();lo();tn();Fn();Jm=new Map});import _u from"node:fs";import Zm from"node:path";import VB from"node:os";async function JB({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(YB,{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:ZS})});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<ZS)break}return r}function XB(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 eT(){try{return JSON.parse(_u.readFileSync(Iu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function QB(e){try{_u.mkdirSync(Zm.dirname(Iu()),{recursive:!0});let t=Iu()+".tmp";_u.writeFileSync(t,JSON.stringify(e)),_u.renameSync(t,Iu())}catch{}}async function tj({fetchImpl:e,now:t=Date.now()}={}){if(Lr.rows&&t-Lr.at<ZB)return Lr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=eT(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await JB({sinceMs:o,cookie:n,fetchImpl:e});Lr={at:t,rows:s.map(XB).filter(Boolean),error:null}}catch(s){Lr={at:t,rows:null,error:String(s?.message||s)}}return Lr.rows}async function nj(e,t){let n=`${e.id}:${t.name}`;if(!Xm.has(n)){let r=await W(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);Xm.set(n,o)}return Xm.get(n)}async function tT(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!QS())return;let r=await tj({fetchImpl:n});if(!r||!r.length)return;let o=await H0(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await nj(e,t),a=eT(),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=yn(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}})}QB({sinceMs:c,seen:[...i].slice(-ej)})}catch{}}var zB,QS,YB,ZS,ZB,Lr,Iu,ej,Qm,Xm,eh=H(()=>{ge();Op();Fn();zB=process.env.BOSTRAT_STATE_DIR||Zm.join(process.env.HOME||VB.tmpdir(),".bostrat"),QS=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,YB="https://cursor.com/api/dashboard/get-filtered-usage-events",ZS=100;ZB=5*6e4,Lr={at:0,rows:null,error:null},Iu=()=>Zm.join(zB,"cursor-usage.json");ej=5e3;Qm=()=>({enabled:QS(),lastPollAt:Lr.at||null,error:Lr.error}),Xm=new Map});import Bi from"node:fs/promises";import Et from"node:path";import rj from"node:os";import oj from"node:net";import{randomUUID as sj}from"node:crypto";import{execFile as aj}from"node:child_process";import{promisify as ij}from"node:util";function mj(e){for(let t of ih(e))if(!iT.test(t))return t;return null}function cT(e){let t=ih(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function hj(e,t){let n=cT(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Et.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Et.resolve(e,n[r+1])}return e}function gj(e){let t=!1,n=!1;for(let r of ih(e)){if(!t){if(iT.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&th(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,th(r))return`serves the filesystem root ("${r}")`;continue}if(th(r))return`serves the filesystem root ("${r}")`;if(wj(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function bj(e,t){if(Et.resolve(e)===Et.resolve(t))return!0;let[n,r]=await Promise.all([$u(e),$u(t)]);return n===r}async function kj(e){let n=(await Bi.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 lT(Et.join(e,o.name,oh))?`./${o.name}`:null))).filter(Boolean)}async function xj(e,t){let[n,r]=await Promise.all([$u(e),$u(t)]),o=Et.relative(n,r);if(o&&(o.startsWith("..")||Et.isAbsolute(o)))throw Pi(`serve directory escapes the Track workspace: ${t}`)}async function vj(e,t){let n=gj(t);if(n)throw Pi(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await yj(e))throw Pi(`serve directory does not exist: ${e}`);let r=mj(t);if(!r)return;if(!r.includes("/")&&!await kb(r,{loginShell:!0,failOpen:!0}))throw Pi(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(dj.test(t)||fj.has(r)||!uj.has(r)||!cT(t).includes("run")||pj.test(t))return;let o=hj(e,t);if(await lT(Et.join(o,oh)))return;let s=await kj(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Pi(`no ${oh} in ${o}${a}`)}async function Zn(){return vn||(nh||=(async()=>{try{vn=JSON.parse(await Bi.readFile(aT,"utf8"))}catch{vn={views:[],adoptWatermarks:{}}}finally{nh=null}return vn.views||=[],vn.adoptWatermarks||={},vn})(),nh)}async function Tn(){await Tt(aT,vn)}function Xn(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 lh(e){return Di.get(e)||null}function dT(e,{name:t,box:n}={}){let r=Di.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 Di.set(e,o),o}function sh(e,t){let n=Di.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Ru(e,{error:t=null}={}){let n=Di.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function fT(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 ah(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function Sj(e,t){let n=ah(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!oT.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!oT.has(s))return s}}function ji(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=oj.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 uh(e){if(process.platform!=="darwin"){await Sn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Sn("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 pT(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function Tj(e,t){let n=new Set(e.views.map(o=>o.port)),r=pT();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=nT;o<=rT;o++)if(!(n.has(o)||r.has(o))&&!await ji(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${nT}-${rT}`)}async function Aj(e){try{let{stdout:t}=await Sn(J,Z("list-sessions","-F","#{session_name}"));return t.split(`
|
|
198
|
+
`).some(n=>n.trim()===zs(e))}catch{return!1}}async function Li(e){try{await Sn(J,Z("kill-session","-t",zs(e)))}catch{}}async function dh(e){if(!await Ka())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=zs(e.slug),n=e.serveCwd??e.cwd,r=Et.dirname(process.execPath);await Sn(J,Z("new-session","-d","-s",t,"-c",n)),await Sn(J,Z("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Sn(J,Z("send-keys","-t",t,"Enter")),await Sn(J,Z("send-keys","-t",t,"-l",e.cmd)),await Sn(J,Z("send-keys","-t",t,"Enter"))}async function sT(e,t=12){try{let{stdout:n}=await Sn(J,Z("capture-pane","-t",zs(e),"-p"));return String(n||"").trimEnd().split(`
|
|
199
199
|
`).filter(Boolean).slice(-t).join(`
|
|
200
|
-
`)}catch{return""}}async function w1(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await m1(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await kS(e.slug)||"server exited before listening"),Gn.delete(e.id),await Vn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Mi(e.port))return Gn.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>h1?(e.state="error",e.error=await kS(e.slug)||"never started listening",await Vn(e,"error",{error:e.error}),!0):!1;let o=(Gn.get(e.id)||0)+1;return Gn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function bu(){if(!Cm){Cm=!0;try{let e=await zn(),t=!1;for(let n of e.views)await w1(n)&&(t=!0);t&&await kn()}finally{Cm=!1}}}async function ku({trackId:e}={}){await bu();let t=await zn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function $S(e){let n=(await zn()).views.find(r=>r.id===e);return n?{...n}:null}function Um(e){return yn&&yn.views.find(t=>t.slug===e)||null}async function Hm({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 u1(r,u),await d1(u,n)},d=await zn();if(i!=="detected"){let w=Pm(t),g=String(n).trim(),k=d.views.find(S=>S.trackId===e&&(S.slug===w||Pm(S.name)===w||S.cmd===g&&At.resolve(S.serveCwd??S.cwd)===u));if(k)return g1(d,k,{cmd:g,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>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);let h={id:zB().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:f1(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await p1(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Fm(h),d.views.push(h),await kn(),await Vn(h,"created",{origin:i}),Nm(e,h.id),i==="detected"&&await Mi(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await kn(),await Vn(h,"live")),{...h}}async function g1(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 c1(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Mi(t.port))return await kn(),Nm(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>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);return t.origin==="detected"?(await Lm(t.port),t.origin="agent"):await Ni(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Gn.delete(t.id),await Fm(t),await kn(),await Vn(t,"created",{restarted:!0,reused:!0}),Nm(t.trackId,t.id),{...t,reused:!0}}async function RS(e){let n=(await zn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Lm(n.port):await Ni(n.slug),n.state!=="stopped"&&(n.state="stopped",Gn.delete(n.id),await kn(),await Vn(n,"stopped")),{...n}}async function OS(e){let t=await zn(),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>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);return await Ni(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Gn.delete(n.id),await Fm(n),await kn(),await Vn(n,"created",{restarted:!0}),{...n}}async function CS(e){let t=await zn(),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 Lm(r.port):await Ni(r.slug),t.views.splice(n,1),Gn.delete(r.id),await kn(),await Vn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function MS(e){let t=await zn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Ni(r.slug),Gn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await kn()),n.length}async function NS(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await zn(),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 kn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(y1)){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(!IS().has(c)&&!n.views.some(l=>l.port===c)&&await Mi(c))try{i.push(await Hm({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 bn,XB,xS,gS,yS,Br,ZB,Hs,bS,Ri,QB,Mm,e1,t1,n1,Dm,vS,Rm,s1,i1,TS,gu,yn,Om,Oi,Bm,ES,Gn,h1,Cm,y1,xu=j(()=>{lt();me();cr();bn=JB(YB),XB=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||GB.tmpdir(),".bostrat"),xS=At.join(XB,"views.json"),[gS,yS]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Br=Number(process.env.BOSTRAT_VIEW_MAX)||3,ZB="bostrat-view-",Hs=e=>`${ZB}${e}`,bS=new Set(["bostrat","www","api","views","app"]),Ri=e=>Object.assign(new Error(e),{status:400}),QB=new Set(["npm","pnpm","yarn","bun"]),Mm="package.json",e1=/[|;&`]|\$\(/,t1=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),n1=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Dm=e=>String(e).trim().split(/\s+/).filter(Boolean),vS=/^[A-Za-z_][A-Za-z0-9_]*=/;Rm=e=>e==="/",s1=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");i1=e=>Ci.stat(e).then(t=>t.isDirectory(),()=>!1),TS=e=>Ci.stat(e).then(t=>t.isFile(),()=>!1),gu=e=>Ci.realpath(e).catch(()=>At.resolve(e));yn=null,Om=null;Oi=new Map,Bm=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,ES=12e3;Gn=new Map,h1=12e4;Cm=!1;y1=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function vu(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>k1?null:n}function x1(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function PS(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await F(e)}catch{return}let r=vu(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await To(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Wm.get(e.id)||0)<b1)return;Wm.set(e.id,s);let a;try{a=await hn(e.cwd,{caller:"reconcile"})}catch{return}let i=await js(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await Se(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await Ot({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 Ln(l,c.number))){if(!x1(u,r))return;let d=await F(e).catch(()=>null);if(d&&vu(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(()=>{}),Wm.delete(e.id)}var b1,Wm,k1,qm=j(()=>{me();Bs();ar();Fn();Si();Eo();b1=6e3,Wm=new Map,k1=30*6e4});import Km from"node:os";import{execFile as v1}from"node:child_process";import{promisify as S1}from"node:util";async function Su(){if(process.platform!=="darwin")return Km.freemem();try{let{stdout:e}=await T1("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:Km.freemem()}catch{return Km.freemem()}}var T1,Gm=j(()=>{T1=S1(v1)});import Ke from"node:fs/promises";import ze from"node:path";import LS from"node:os";import{randomUUID as E1}from"node:crypto";import{execFile as A1}from"node:child_process";import{promisify as _1}from"node:util";import{chromium as I1}from"playwright-core";async function Sn(){return Io||(Vm||=(async()=>{try{Io=JSON.parse(await Ke.readFile(US,"utf8"))}catch{Io={mocks:[]}}finally{Vm=null}return Io.mocks||=[],Io})(),Vm)}async function Ht(){await Tt(US,Io)}function KS(e,t){vn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&vn(e,t.v,"png")}function yt(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 Wt(e,t){let n=Di(e.cwd);return{html:ze.join(n,`${e.id}.v${t}.html`),png:ze.join(n,`${e.id}.v${t}.png`)}}function Pi(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?ze.join(e.cwd,r):null}function tn(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 Jm(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Tu(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(P1)}function D1(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 B1(e){let t=Buffer.alloc(24),n=await Ke.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function j1({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 L1(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let h=0;h<s;h++)for(let w=h*o*4,g=0;g<o;g++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(g<u&&(u=g),g>d&&(d=g),h<f&&(f=h),p=h);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},t.toString("base64"))}async function F1(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 U1({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await I1.launch({executablePath:HS,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ct("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ws})).newPage();a.setDefaultTimeout(qs),await a.goto(`file://${e}`,{waitUntil:"load",timeout:qs}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:qs});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await L1(a,i),u=l&&j1({...l,pad:O1*Ws,align:Ws});u&&(i=await F1(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<WS)throw new Ct(`capture produced a ${i.length}-byte image`);let c=Jm(i);if(!c)throw new Ct("output is not a PNG");return await Ke.writeFile(t,i),{width:Math.round(c.width/Ws),height:Math.round(c.height/Ws),dpr:Ws,renderMs:Date.now()-r}}catch(s){throw s instanceof Ct?s:new Ct(/timeout/i.test(s?.message||"")?`render timed out after ${qs}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function jS({htmlPath:e,outPath:t,width:n}){let r=await Ke.mkdtemp(ze.join(LS.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Ym(DS,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:qs,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Ct(`firefox not found (${DS})`,""):new Ct(c?.killed?`render timed out after ${qs}ms`:"firefox exited non-zero",s)}let a=await Ke.stat(t).catch(()=>null);if(!a||a.size<WS)throw new Ct(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Jm(await B1(t));if(!i)throw new Ct("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Ke.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function H1(e){if($1==="firefox")return jS(e);try{return await U1(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await jS(e)}catch(n){throw/not found/.test(n?.message||"")?new Ct(`no browser installed \u2014 chromium missing at ${HS} 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 GS(e){return zm=zm.then(e,e),zm}async function VS(e,t){let n=await Sn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Ht(),await yt(r,"rendering",{version:t});try{let o=await Su();if(o<C1)throw new Ct(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Wt(r,t),a=await W1({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Ke.rm(s.png,{force:!0}).catch(()=>{});return}Xm(r,{v:t,source:"html",htmlRel:ze.relative(r.cwd,s.html),pngRel:ze.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Ht(),await yt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),xn(r),vn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Ht(),await yt(r,"error",{version:t,error:r.error}),xn(r)}}}function Xm(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 q1({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?R1:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Bi({trackId:e,group:t}={}){let n=await Sn(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function Yn(e){let n=(await Sn()).mocks.find(r=>r.id===e);return n?{...n}:null}function zS({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&&!Tu(t))throw new Error("image must be a PNG");if(n){let o=D1(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>BS)throw new Error(`video must be ${BS/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function YS({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=q1({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=zS({html:n,image:r,video:o}),h=await Sn(),w={id:E1().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 Ke.mkdir(Di(s),{recursive:!0}),h.mocks.push(w),d?(await Ke.writeFile(Wt(w,1).html,n),await Ht(),await yt(w,"created"),xn(w),vn(w,1,"html"),GS(()=>VS(w.id,1))):o?(await XS(w,1,o,p),await Ht(),await yt(w,"created"),await yt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),xn(w),KS(w,w.versions[0])):(await ZS(w,1,r),await Ht(),await yt(w,"created"),await yt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),xn(w),vn(w,1,"png")),{...w}}async function JS(e,{html:t,image:n,video:r,width:o}={}){let a=(await Sn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=zS({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 Ke.mkdir(Di(a.cwd),{recursive:!0}),i?(a.state="queued",await Ke.writeFile(Wt(a,l).html,t),await Ht(),await yt(a,"updated",{version:l}),xn(a),vn(a,l,"html"),GS(()=>VS(a.id,l))):r?(await XS(a,l,r,c),await Ht(),await yt(a,"updated",{version:l}),await yt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:tn(a)?.durationS??null}),xn(a),KS(a,a.versions.find(u=>u.v===l))):(await ZS(a,l,n),await Ht(),await yt(a,"updated",{version:l}),await yt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),xn(a),vn(a,l,"png")),{...a}}async function K1(e){try{let{stdout:t}=await Ym(M1,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:qS,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 G1(e,t){try{await Ym(N1,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:qS,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Ke.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function XS(e,t,n,r){let o=Di(e.cwd),s=ze.join(o,`${e.id}.v${t}.${r.ext}`),a=ze.join(o,`${e.id}.v${t}.poster.png`);await Ke.writeFile(s,n);let i=await K1(s),c=await G1(s,a);Xm(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:ze.relative(e.cwd,s),posterRel:c?ze.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 ZS(e,t,n){let r=Jm(n);if(!r)throw new Error("image must be a PNG");let o=Wt(e,t);await Ke.writeFile(o.png,n),Xm(e,{v:t,source:"image",htmlRel:null,pngRel:ze.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function QS(e){let t=await Sn(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await yt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function eT(e){let t=await Sn(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Wt(r,o);await Ke.rm(s.html,{force:!0}).catch(()=>{}),await Ke.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Ke.rm(ze.join(r.cwd,s),{force:!0}).catch(()=>{});return await Ht(),await yt(r,"deleted"),Vt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function tT(e){let n=(await Sn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=ze.join(FS,"mock-archive",e.id),o=Di(e.cwd),s=ze.join(r,".bostrat","mocks");await Ke.mkdir(s,{recursive:!0});let a=await Ke.readdir(o).catch(()=>[]);for(let i of a)await Ke.copyFile(ze.join(o,i),ze.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 Ke.stat(ze.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(),xn(i)}return await Ht(),n.length}async function nT(e){let t=await Sn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Ht();for(let r of n)Vt("mocks",r.id).catch(()=>{})}return n.length}async function rT(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Sn();for(let r of n.mocks){await xn(r);for(let o of r.versions||[])o.htmlRel&&await vn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await vn(r,o.v,"png"),o.videoRel&&await vn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Ym,FS,US,$1,HS,DS,R1,Ws,O1,WS,qs,C1,BS,M1,N1,qS,Ct,Io,Vm,xn,vn,Di,P1,W1,zm,Ks=j(()=>{me();cr();Gm();ur();Ym=_1(A1),FS=process.env.BOSTRAT_STATE_DIR||ze.join(process.env.HOME||LS.tmpdir(),".bostrat"),US=ze.join(FS,"mocks.json"),$1=process.env.BOSTRAT_MOCK_ENGINE||"chromium",HS=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",DS=process.env.BOSTRAT_FIREFOX_BIN||"firefox",R1=1300,Ws=2,O1=32,WS=1500,qs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,C1=700*1024*1024,BS=100*1024*1024,M1=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",N1=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",qS=2e4,Ct=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
200
|
+
`)}catch{return""}}async function _j(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await Aj(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await sT(e.slug)||"server exited before listening"),Jn.delete(e.id),await Xn(e,e.state,e.error?{error:e.error}:{}),!0}if(await ji(e.port))return Jn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Xn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>Ej?(e.state="error",e.error=await sT(e.slug)||"never started listening",await Xn(e,"error",{error:e.error}),!0):!1;let o=(Jn.get(e.id)||0)+1;return Jn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Ou(){if(!rh){rh=!0;try{let e=await Zn(),t=!1;for(let n of e.views)await _j(n)&&(t=!0);t&&await Tn()}finally{rh=!1}}}async function Cu({trackId:e}={}){await Ou();let t=await Zn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function mT(e){let n=(await Zn()).views.find(r=>r.id===e);return n?{...n}:null}function fh(e){return vn&&vn.views.find(t=>t.slug===e)||null}async function ph({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=Et.resolve(r,o??"."),f=i==="detected"?null:async()=>{await xj(r,u),await vj(u,n)},d=await Zn();if(i!=="detected"){let w=ah(t),g=String(n).trim(),b=d.views.find(v=>v.trackId===e&&(v.slug===w||ah(v.name)===w||v.cmd===g&&Et.resolve(v.serveCwd??v.cwd)===u));if(b)return Ij(d,b,{cmd:g,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>=Fr)throw new Error(`view cap reached (${Fr} live) \u2014 stop one first`);let h={id:sj().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:Sj(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Tj(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await dh(h),d.views.push(h),await Tn(),await Xn(h,"created",{origin:i}),sh(e,h.id),i==="detected"&&await ji(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await Tn(),await Xn(h,"live")),{...h}}async function Ij(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 bj(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await ji(t.port))return await Tn(),sh(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>=Fr)throw new Error(`view cap reached (${Fr} live) \u2014 stop one first`);return t.origin==="detected"?(await uh(t.port),t.origin="agent"):await Li(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Jn.delete(t.id),await dh(t),await Tn(),await Xn(t,"created",{restarted:!0,reused:!0}),sh(t.trackId,t.id),{...t,reused:!0}}async function hT(e){let n=(await Zn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await uh(n.port):await Li(n.slug),n.state!=="stopped"&&(n.state="stopped",Jn.delete(n.id),await Tn(),await Xn(n,"stopped")),{...n}}async function wT(e){let t=await Zn(),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>=Fr)throw new Error(`view cap reached (${Fr} live) \u2014 stop one first`);return await Li(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Jn.delete(n.id),await dh(n),await Tn(),await Xn(n,"created",{restarted:!0}),{...n}}async function gT(e){let t=await Zn(),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 uh(r.port):await Li(r.slug),t.views.splice(n,1),Jn.delete(r.id),await Tn(),await Xn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function yT(e){let t=await Zn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Li(r.slug),Jn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Tn()),n.length}async function bT(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Zn(),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 Tn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll($j)){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(!pT().has(c)&&!n.views.some(l=>l.port===c)&&await ji(c))try{i.push(await ph({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 Sn,cj,aT,nT,rT,Fr,lj,zs,oT,Pi,uj,oh,dj,fj,pj,ih,iT,th,wj,yj,lT,$u,vn,nh,Di,ch,uT,Jn,Ej,rh,$j,Mu=H(()=>{ut();ge();fr();Sn=ij(aj),cj=process.env.BOSTRAT_STATE_DIR||Et.join(process.env.HOME||rj.tmpdir(),".bostrat"),aT=Et.join(cj,"views.json"),[nT,rT]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Fr=Number(process.env.BOSTRAT_VIEW_MAX)||3,lj="bostrat-view-",zs=e=>`${lj}${e}`,oT=new Set(["bostrat","www","api","views","app"]),Pi=e=>Object.assign(new Error(e),{status:400}),uj=new Set(["npm","pnpm","yarn","bun"]),oh="package.json",dj=/[|;&`]|\$\(/,fj=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),pj=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,ih=e=>String(e).trim().split(/\s+/).filter(Boolean),iT=/^[A-Za-z_][A-Za-z0-9_]*=/;th=e=>e==="/",wj=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");yj=e=>Bi.stat(e).then(t=>t.isDirectory(),()=>!1),lT=e=>Bi.stat(e).then(t=>t.isFile(),()=>!1),$u=e=>Bi.realpath(e).catch(()=>Et.resolve(e));vn=null,nh=null;Di=new Map,ch=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,uT=12e3;Jn=new Map,Ej=12e4;rh=!1;$j=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Nu(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>Oj?null:n}function Cj(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Pu(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await W(e)}catch{return}let o=Nu(r);if(o==null)return;let s=(e.boxes||[]).find(f=>f.name===e.leadBox);if(await _o(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(mh.get(e.id)||0)<Rj)return;mh.set(e.id,a);let i;try{i=await kn(e.cwd,{caller:"reconcile"})}catch{return}let c=await Ws(e).catch(()=>[n]),l=(i||[]).find(f=>c.includes(f.headRefName));if(!l)return;let u=await Ee(e.cwd).catch(()=>null);if(u){let{link:f,created:d}=await Rt({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!d&&!(f?.trackId===e.id&&await Hn(u,l.number))){if(!Cj(f,o))return;let p=await W(e).catch(()=>null);if(p&&Nu(p)==null)return;await $(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:l.number,url:l.url,title:l.title,branch:n,repo:u}});return}}await $(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),mh.delete(e.id)}var Rj,mh,Oj,Du=H(()=>{ge();Eo();ur();Wn();_i();Io();Rj=6e3,mh=new Map,Oj=30*6e4});import hh from"node:os";import{execFile as Mj}from"node:child_process";import{promisify as Nj}from"node:util";async function Bu(){if(process.platform!=="darwin")return hh.freemem();try{let{stdout:e}=await Pj("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:hh.freemem()}catch{return hh.freemem()}}var Pj,wh=H(()=>{Pj=Nj(Mj)});import Ye from"node:fs/promises";import Je from"node:path";import ST from"node:os";import{randomUUID as Dj}from"node:crypto";import{execFile as Bj}from"node:child_process";import{promisify as jj}from"node:util";import{chromium as Lj}from"playwright-core";async function _n(){return Oo||(gh||=(async()=>{try{Oo=JSON.parse(await Ye.readFile(AT,"utf8"))}catch{Oo={mocks:[]}}finally{gh=null}return Oo.mocks||=[],Oo})(),gh)}async function qt(){await Tt(AT,Oo)}function $T(e,t){En(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&En(e,t.v,"png")}function bt(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 Gt(e,t){let n=Ui(e.cwd);return{html:Je.join(n,`${e.id}.v${t}.html`),png:Je.join(n,`${e.id}.v${t}.png`)}}function Fi(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Je.join(e.cwd,r):null}function sn(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 kh(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function ju(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(Kj)}function Vj(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 zj(e){let t=Buffer.alloc(24),n=await Ye.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function Yj({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 Jj(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let h=0;h<s;h++)for(let w=h*o*4,g=0;g<o;g++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(g<u&&(u=g),g>d&&(d=g),h<f&&(f=h),p=h);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},t.toString("base64"))}async function Xj(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 Zj({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await Lj.launch({executablePath:ET,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ot("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ys})).newPage();a.setDefaultTimeout(Js),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Js}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Js});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await Jj(a,i),u=l&&Yj({...l,pad:Hj*Ys,align:Ys});u&&(i=await Xj(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<_T)throw new Ot(`capture produced a ${i.length}-byte image`);let c=kh(i);if(!c)throw new Ot("output is not a PNG");return await Ye.writeFile(t,i),{width:Math.round(c.width/Ys),height:Math.round(c.height/Ys),dpr:Ys,renderMs:Date.now()-r}}catch(s){throw s instanceof Ot?s:new Ot(/timeout/i.test(s?.message||"")?`render timed out after ${Js}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function vT({htmlPath:e,outPath:t,width:n}){let r=await Ye.mkdtemp(Je.join(ST.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await bh(kT,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Js,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Ot(`firefox not found (${kT})`,""):new Ot(c?.killed?`render timed out after ${Js}ms`:"firefox exited non-zero",s)}let a=await Ye.stat(t).catch(()=>null);if(!a||a.size<_T)throw new Ot(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=kh(await zj(t));if(!i)throw new Ot("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Ye.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function Qj(e){if(Fj==="firefox")return vT(e);try{return await Zj(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await vT(e)}catch(n){throw/not found/.test(n?.message||"")?new Ot(`no browser installed \u2014 chromium missing at ${ET} 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 RT(e){return yh=yh.then(e,e),yh}async function OT(e,t){let n=await _n(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await qt(),await bt(r,"rendering",{version:t});try{let o=await Bu();if(o<Wj)throw new Ot(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Gt(r,t),a=await eL({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Ye.rm(s.png,{force:!0}).catch(()=>{});return}xh(r,{v:t,source:"html",htmlRel:Je.relative(r.cwd,s.html),pngRel:Je.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await qt(),await bt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),An(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 qt(),await bt(r,"error",{version:t,error:r.error}),An(r)}}}function xh(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 tL({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?Uj:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Hi({trackId:e,group:t}={}){let n=await _n(),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 Qn(e){let n=(await _n()).mocks.find(r=>r.id===e);return n?{...n}:null}function CT({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&&!ju(t))throw new Error("image must be a PNG");if(n){let o=Vj(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>xT)throw new Error(`video must be ${xT/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function MT({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=tL({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=CT({html:n,image:r,video:o}),h=await _n(),w={id:Dj().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 Ye.mkdir(Ui(s),{recursive:!0}),h.mocks.push(w),d?(await Ye.writeFile(Gt(w,1).html,n),await qt(),await bt(w,"created"),An(w),En(w,1,"html"),RT(()=>OT(w.id,1))):o?(await PT(w,1,o,p),await qt(),await bt(w,"created"),await bt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),An(w),$T(w,w.versions[0])):(await DT(w,1,r),await qt(),await bt(w,"created"),await bt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),An(w),En(w,1,"png")),{...w}}async function NT(e,{html:t,image:n,video:r,width:o}={}){let a=(await _n()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=CT({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 Ye.mkdir(Ui(a.cwd),{recursive:!0}),i?(a.state="queued",await Ye.writeFile(Gt(a,l).html,t),await qt(),await bt(a,"updated",{version:l}),An(a),En(a,l,"html"),RT(()=>OT(a.id,l))):r?(await PT(a,l,r,c),await qt(),await bt(a,"updated",{version:l}),await bt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:sn(a)?.durationS??null}),An(a),$T(a,a.versions.find(u=>u.v===l))):(await DT(a,l,n),await qt(),await bt(a,"updated",{version:l}),await bt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),An(a),En(a,l,"png")),{...a}}async function nL(e){try{let{stdout:t}=await bh(qj,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:IT,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 rL(e,t){try{await bh(Gj,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:IT,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Ye.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function PT(e,t,n,r){let o=Ui(e.cwd),s=Je.join(o,`${e.id}.v${t}.${r.ext}`),a=Je.join(o,`${e.id}.v${t}.poster.png`);await Ye.writeFile(s,n);let i=await nL(s),c=await rL(s,a);xh(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Je.relative(e.cwd,s),posterRel:c?Je.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function DT(e,t,n){let r=kh(n);if(!r)throw new Error("image must be a PNG");let o=Gt(e,t);await Ye.writeFile(o.png,n),xh(e,{v:t,source:"image",htmlRel:null,pngRel:Je.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function BT(e){let t=await _n(),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 bt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function jT(e){let t=await _n(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Gt(r,o);await Ye.rm(s.html,{force:!0}).catch(()=>{}),await Ye.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Ye.rm(Je.join(r.cwd,s),{force:!0}).catch(()=>{});return await qt(),await bt(r,"deleted"),Xt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function LT(e){let n=(await _n()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Je.join(TT,"mock-archive",e.id),o=Ui(e.cwd),s=Je.join(r,".bostrat","mocks");await Ye.mkdir(s,{recursive:!0});let a=await Ye.readdir(o).catch(()=>[]);for(let i of a)await Ye.copyFile(Je.join(o,i),Je.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Ye.stat(Je.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),An(i)}return await qt(),n.length}async function FT(e){let t=await _n(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await qt();for(let r of n)Xt("mocks",r.id).catch(()=>{})}return n.length}async function UT(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await rt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await _n();for(let r of n.mocks){await An(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 bh,TT,AT,Fj,ET,kT,Uj,Ys,Hj,_T,Js,Wj,xT,qj,Gj,IT,Ot,Oo,gh,An,En,Ui,Kj,eL,yh,Xs=H(()=>{ge();fr();wh();mr();bh=jj(Bj),TT=process.env.BOSTRAT_STATE_DIR||Je.join(process.env.HOME||ST.tmpdir(),".bostrat"),AT=Je.join(TT,"mocks.json"),Fj=process.env.BOSTRAT_MOCK_ENGINE||"chromium",ET=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",kT=process.env.BOSTRAT_FIREFOX_BIN||"firefox",Uj=1300,Ys=2,Hj=32,_T=1500,Js=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,Wj=700*1024*1024,xT=100*1024*1024,qj=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",Gj=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",IT=2e4,Ot=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
201
201
|
${n.trim().split(`
|
|
202
202
|
`).slice(-6).join(`
|
|
203
|
-
`)}`:t),this.name="MockRenderError"}},Io=null,Vm=null;xn=e=>wt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),vn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Pi(e,r,"video"):r&&Pi(e,r,n)||(n==="html"?Wt(e,t).html:null);return o?lr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Di=e=>ze.join(e,".bostrat","mocks");P1=Buffer.from([137,80,78,71,13,10,26,10]);W1=H1,zm=Promise.resolve()});import V1 from"node:fs/promises";import cT from"node:path";import z1 from"node:os";import{randomUUID as lT}from"node:crypto";async function Xn(){return Jn||(Zm||=(async()=>{let e=!0;try{Jn=JSON.parse(await V1.readFile(uT,"utf8"))}catch{Jn={flows:[]},e=!1}finally{Zm=null}if(Jn.flows||=[],!e&&Ne()){let{records:t,source:n}=await et("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Jn.flows.push(r.data);Jn.flows.length&&await Gs().catch(()=>{})}}return Jn})(),Zm)}async function Gs(){await Tt(uT,Jn)}function Qm(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 dT({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>oT)throw new Error(`steps must be an array of 1-${oT}`);let r=await Bi({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>aT)throw new Error(`${l}: caption over ${aT} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>iT)throw new Error(`${l}: title over ${iT} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===d);if(p.length||(p=r.filter(S=>S.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let h=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(S=>S.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let g=o.findIndex(S=>S.mockId===h.id),k=g>=0?o.splice(g,1)[0].id:lT().slice(0,8);s.push({id:k,mockId:h.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function J1(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>sT)throw new Error(`name over ${sT} chars`);return e.trim()}async function fT({trackId:e}={}){let t=await Xn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function X1(e){let n=(await Xn()).flows.find(r=>r.id===e);return n?{...n}:null}async function pT(e){let t=await X1(e);if(!t)return null;let n=new Map((await Bi({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=tn(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 mT({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=J1(t),i=await Xn(),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 dT({trackId:e,steps:n}),f=Date.now(),d={id:lT().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 Gs(),await Qm(d,"created"),Eu(d),{...d}}async function hT(e,{steps:t,note:n}={}){let o=(await Xn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await dT({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 Gs(),await Qm(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Eu(o),{...o}}async function wT(e){let t=await Xn(),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 Gs(),await Qm(r,"deleted"),Vt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function gT(e){return(await Xn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function yT(e){let n=(await Xn()).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(),Eu(r);return n.length&&await Gs(),n.length}async function bT(e){let t=await Xn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Gs();for(let r of n)Vt("flows",r.id).catch(()=>{})}return n.length}async function kT(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Xn();for(let r of n.flows)await Eu(r);return{pushed:n.flows.length,source:"plane"}}var Y1,uT,oT,sT,aT,iT,Jn,Zm,Eu,eh=j(()=>{me();cr();Ks();ur();Y1=process.env.BOSTRAT_STATE_DIR||cT.join(process.env.HOME||z1.tmpdir(),".bostrat"),uT=cT.join(Y1,"flows.json"),oT=20,sT=80,aT=500,iT=80,Jn=null,Zm=null;Eu=e=>wt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import er from"node:fs/promises";import tr from"node:path";import Z1 from"node:os";import{randomUUID as Q1}from"node:crypto";function ji(e){return ej.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Tn(){return Zn||(th||=(async()=>{let e=!0;try{Zn=JSON.parse(await er.readFile(vT,"utf8"))}catch{Zn={runbooks:[]},e=!1}finally{th=null}return Zn.runbooks||=[],!e&&Ne()&&await tj().catch(()=>{}),Zn})(),th)}async function Ro(){await Tt(vT,Zn)}function _u(e){return Li(wt("runbooks",{...e},{trackId:e.trackId}))}async function tj(){let{records:e,source:t}=await et("runbooks");if(!(t!=="plane"||!e.length)){await er.mkdir(Au,{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 Pa("runbooks",r.id,o.v,"md");typeof s=="string"&&await er.writeFile(Qn(r,o.v),s).catch(()=>{})}Zn.runbooks.push(r)}Zn.runbooks.length&&await Ro()}}function Vs(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 Qn(e,t){return tr.join(Au,`${e.id}.v${t}.md`)}function ST(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 TT(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>xT)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${xT/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=ji(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 nj(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function rj({trackId:e,status:t}={}){let n=await Tn(),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 ET({trackId:e,status:t}={}){let n=await Tn(),r=await rj({trackId:e,status:t});if(!Ne())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await et("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 nh(e){let t=await oj(e);if(t)return t;if(!Ne())return null;let{records:n,source:r}=await et("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 AT(e,t){let n=await Pa("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function oj(e){let n=(await Tn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function _T({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=TT(r),c=await Tn(),l={id:Q1().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 er.mkdir(Au,{recursive:!0}),await er.writeFile(Qn(l,1),r),l.versions.push({v:1,mdRel:tr.relative($o,Qn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Ro(),await Vs(l,"created",{version:1}),_u(l),Li(lr("runbooks",l.id,1,"md",Qn(l,1))),{...l}}async function IT(e,{md:t,note:n}={}){let o=(await Tn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");nj(o,"update");let s=TT(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await er.mkdir(Au,{recursive:!0}),await er.writeFile(Qn(o,a),t),o.versions.push({v:a,mdRel:tr.relative($o,Qn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Ro(),await Vs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),_u(o),Li(lr("runbooks",o.id,a,"md",Qn(o,a))),{...o}}async function $T(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Tn()).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 Vs(r,t==="done"?"done":"reopened"),_u(r),{...r})}async function RT(e){let t=await Tn(),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 er.rm(tr.join($o,o.mdRel),{force:!0}).catch(()=>{});return await Ro(),r.trackDeletedAt||await Vs(r,"deleted"),Li(Vt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function OT(e){let n=(await Tn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Vs(r,"orphaned"),r.cwd=null,_u(r);return n.length&&await Ro(),n.length}async function CT(e){let t=await Tn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Vs(r,"deleted");for(let o of r.versions||[])await er.rm(tr.join($o,o.mdRel),{force:!0}).catch(()=>{});Li(Vt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Ro()),n.length}async function MT(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Tn();for(let r of n.runbooks){await wt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await lr("runbooks",r.id,o.v,"md",tr.join($o,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var $o,vT,Au,xT,ej,Zn,th,Li,Iu=j(()=>{cr();me();ur();$o=process.env.BOSTRAT_STATE_DIR||tr.join(process.env.HOME||Z1.tmpdir(),".bostrat"),vT=tr.join($o,"runbooks.json"),Au=tr.join($o,"runbooks"),xT=256*1024,ej=[{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/}];Zn=null,th=null;Li=e=>Promise.resolve(e).catch(()=>{})});import rh from"node:fs/promises";import Fi from"node:path";import{randomUUID as sj}from"node:crypto";function sh(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return aj.has(t)?"image":ij.has(t)?"doc":null}function cj(e){let r=Fi.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 DT(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>NT)throw new Error(`file too large \u2014 max ${Math.floor(NT/(1024*1024))} MB`);let r=cj(t),o=Fi.extname(r).slice(1),s=sh(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=sj().slice(0,8),i=oh(e);await rh.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await rh.writeFile(Fi.join(i,c),n,{mode:384}),await rh.chmod(Fi.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${PT}/${c}`,kind:s,size:n.length}}function BT(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function ah(e){return e.role==="user"&&lj.test((e.text||"").trim())}var PT,oh,NT,aj,ij,lj,ih=j(()=>{PT=".bostrat/uploads",oh=e=>Fi.join(e,PT),NT=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,aj=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),ij=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"]);lj=/\[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 jT=j(()=>{});import{execFile as uj}from"node:child_process";import{promisify as dj}from"node:util";function ch(e){return`refs/bostrat/wip/${e}`}function mj(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function UT(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function hj(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:pj}async function lh(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await jr(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await jr(o,["remote","get-url","origin"])}catch{return LT.has(o)||(LT.add(o),t.log?.(`[wip-sync] ${r}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let s=await As(o,"bostrat: wip snapshot"),{stdout:a}=await jr(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&$u.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=ch(r);return await jr(o,["push","--force","origin",`${s}:${c}`],FT),$u.set(o,i),await $(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:c,sha:s,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${r}: pushed ${s.slice(0,7)} \u2192 ${c}`),{pushed:!0,sha:s,ref:c}}catch(s){return t.log?.(`[wip-sync] ${r}: push failed (will retry): ${String(s?.message||s).split(`
|
|
204
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
205
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
203
|
+
`)}`:t),this.name="MockRenderError"}},Oo=null,gh=null;An=e=>gt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),En=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Fi(e,r,"video"):r&&Fi(e,r,n)||(n==="html"?Gt(e,t).html:null);return o?pr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ui=e=>Je.join(e,".bostrat","mocks");Kj=Buffer.from([137,80,78,71,13,10,26,10]);eL=Qj,yh=Promise.resolve()});import oL from"node:fs/promises";import KT from"node:path";import sL from"node:os";import{randomUUID as VT}from"node:crypto";async function tr(){return er||(vh||=(async()=>{let e=!0;try{er=JSON.parse(await oL.readFile(zT,"utf8"))}catch{er={flows:[]},e=!1}finally{vh=null}if(er.flows||=[],!e&&je()){let{records:t,source:n}=await rt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&er.flows.push(r.data);er.flows.length&&await Zs().catch(()=>{})}}return er})(),vh)}async function Zs(){await Tt(zT,er)}function Sh(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 YT({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>HT)throw new Error(`steps must be an array of 1-${HT}`);let r=await Hi({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>qT)throw new Error(`${l}: caption over ${qT} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>GT)throw new Error(`${l}: title over ${GT} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(v=>v.id===d);if(p.length||(p=r.filter(v=>v.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(v=>`mock:${v.id}`).join(", ")}) \u2014 use the id`);let h=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(v=>v.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let g=o.findIndex(v=>v.mockId===h.id),b=g>=0?o.splice(g,1)[0].id:VT().slice(0,8);s.push({id:b,mockId:h.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function iL(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>WT)throw new Error(`name over ${WT} chars`);return e.trim()}async function JT({trackId:e}={}){let t=await tr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function cL(e){let n=(await tr()).flows.find(r=>r.id===e);return n?{...n}:null}async function XT(e){let t=await cL(e);if(!t)return null;let n=new Map((await Hi({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=sn(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 ZT({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=iL(t),i=await tr(),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 YT({trackId:e,steps:n}),f=Date.now(),d={id:VT().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 Zs(),await Sh(d,"created"),Lu(d),{...d}}async function QT(e,{steps:t,note:n}={}){let o=(await tr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await YT({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 Zs(),await Sh(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Lu(o),{...o}}async function eA(e){let t=await tr(),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 Zs(),await Sh(r,"deleted"),Xt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function tA(e){return(await tr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function nA(e){let n=(await tr()).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(),Lu(r);return n.length&&await Zs(),n.length}async function rA(e){let t=await tr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Zs();for(let r of n)Xt("flows",r.id).catch(()=>{})}return n.length}async function oA(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await rt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tr();for(let r of n.flows)await Lu(r);return{pushed:n.flows.length,source:"plane"}}var aL,zT,HT,WT,qT,GT,er,vh,Lu,Th=H(()=>{ge();fr();Xs();mr();aL=process.env.BOSTRAT_STATE_DIR||KT.join(process.env.HOME||sL.tmpdir(),".bostrat"),zT=KT.join(aL,"flows.json"),HT=20,WT=80,qT=500,GT=80,er=null,vh=null;Lu=e=>gt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import or from"node:fs/promises";import sr from"node:path";import lL from"node:os";import{randomUUID as uL}from"node:crypto";function Wi(e){return dL.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function In(){return nr||(Ah||=(async()=>{let e=!0;try{nr=JSON.parse(await or.readFile(aA,"utf8"))}catch{nr={runbooks:[]},e=!1}finally{Ah=null}return nr.runbooks||=[],!e&&je()&&await fL().catch(()=>{}),nr})(),Ah)}async function Mo(){await Tt(aA,nr)}function Uu(e){return qi(gt("runbooks",{...e},{trackId:e.trackId}))}async function fL(){let{records:e,source:t}=await rt("runbooks");if(!(t!=="plane"||!e.length)){await or.mkdir(Fu,{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 La("runbooks",r.id,o.v,"md");typeof s=="string"&&await or.writeFile(rr(r,o.v),s).catch(()=>{})}nr.runbooks.push(r)}nr.runbooks.length&&await Mo()}}function Qs(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 rr(e,t){return sr.join(Fu,`${e.id}.v${t}.md`)}function iA(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 cA(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>sA)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${sA/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Wi(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 pL(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function mL({trackId:e,status:t}={}){let n=await In(),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 lA({trackId:e,status:t}={}){let n=await In(),r=await mL({trackId:e,status:t});if(!je())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await rt("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 Eh(e){let t=await hL(e);if(t)return t;if(!je())return null;let{records:n,source:r}=await rt("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 uA(e,t){let n=await La("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function hL(e){let n=(await In()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function dA({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=cA(r),c=await In(),l={id:uL().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 or.mkdir(Fu,{recursive:!0}),await or.writeFile(rr(l,1),r),l.versions.push({v:1,mdRel:sr.relative(Co,rr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Mo(),await Qs(l,"created",{version:1}),Uu(l),qi(pr("runbooks",l.id,1,"md",rr(l,1))),{...l}}async function fA(e,{md:t,note:n}={}){let o=(await In()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");pL(o,"update");let s=cA(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await or.mkdir(Fu,{recursive:!0}),await or.writeFile(rr(o,a),t),o.versions.push({v:a,mdRel:sr.relative(Co,rr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Mo(),await Qs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Uu(o),qi(pr("runbooks",o.id,a,"md",rr(o,a))),{...o}}async function pA(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await In()).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 Mo(),await Qs(r,t==="done"?"done":"reopened"),Uu(r),{...r})}async function mA(e){let t=await In(),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 or.rm(sr.join(Co,o.mdRel),{force:!0}).catch(()=>{});return await Mo(),r.trackDeletedAt||await Qs(r,"deleted"),qi(Xt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function hA(e){let n=(await In()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Qs(r,"orphaned"),r.cwd=null,Uu(r);return n.length&&await Mo(),n.length}async function wA(e){let t=await In(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Qs(r,"deleted");for(let o of r.versions||[])await or.rm(sr.join(Co,o.mdRel),{force:!0}).catch(()=>{});qi(Xt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Mo()),n.length}async function gA(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await rt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await In();for(let r of n.runbooks){await gt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await pr("runbooks",r.id,o.v,"md",sr.join(Co,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Co,aA,Fu,sA,dL,nr,Ah,qi,Hu=H(()=>{fr();ge();mr();Co=process.env.BOSTRAT_STATE_DIR||sr.join(process.env.HOME||lL.tmpdir(),".bostrat"),aA=sr.join(Co,"runbooks.json"),Fu=sr.join(Co,"runbooks"),sA=256*1024,dL=[{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/}];nr=null,Ah=null;qi=e=>Promise.resolve(e).catch(()=>{})});import _h from"node:fs/promises";import Gi from"node:path";import{randomUUID as wL}from"node:crypto";function $h(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return gL.has(t)?"image":yL.has(t)?"doc":null}function bL(e){let r=Gi.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 kA(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>yA)throw new Error(`file too large \u2014 max ${Math.floor(yA/(1024*1024))} MB`);let r=bL(t),o=Gi.extname(r).slice(1),s=$h(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=wL().slice(0,8),i=Ih(e);await _h.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await _h.writeFile(Gi.join(i,c),n,{mode:384}),await _h.chmod(Gi.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${bA}/${c}`,kind:s,size:n.length}}function xA(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Rh(e){return e.role==="user"&&kL.test((e.text||"").trim())}var bA,Ih,yA,gL,yL,kL,Oh=H(()=>{bA=".bostrat/uploads",Ih=e=>Gi.join(e,bA),yA=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,gL=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),yL=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"]);kL=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function No(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var vA=H(()=>{});import{execFile as xL}from"node:child_process";import{promisify as vL}from"node:util";function Ch(e){return`refs/bostrat/wip/${e}`}function AL(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function AA(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function EL(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:TL}async function Mh(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await Ur(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Ur(o,["remote","get-url","origin"])}catch{return SA.has(o)||(SA.add(o),t.log?.(`[wip-sync] ${r}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let s=await Cs(o,"bostrat: wip snapshot"),{stdout:a}=await Ur(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&Wu.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=Ch(r);return await Ur(o,["push","--force","origin",`${s}:${c}`],TA),Wu.set(o,i),await $(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:c,sha:s,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${r}: pushed ${s.slice(0,7)} \u2192 ${c}`),{pushed:!0,sha:s,ref:c}}catch(s){return t.log?.(`[wip-sync] ${r}: push failed (will retry): ${String(s?.message||s).split(`
|
|
204
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Nh(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Ur(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Ur(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await Cs(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await Ur(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Wu.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=AL(n);return await Ur(r,["push","origin",`${o}:${i}`],TA),Wu.set(r,a),await $(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,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(`
|
|
205
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function EA({listTracks:e,sweepMs:t=EL(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)Ge(i)||i?.kind==="setup"||await Mh(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 SL,TL,TA,Ur,Wu,SA,Ph=H(()=>{Hl();ge();bn();SL=vL(xL),TL=12e4,TA=6e4;Ur=(e,t,n=15e3)=>SL("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Wu=new Map,SA=new Set});import _L from"node:fs";import Dh from"node:fs/promises";import IL from"node:path";function CL(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}async function _A(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(!CL(t))return{restored:!1,reason:"plane not configured"};let s=as(e),a=IL.join(s,`${o}.ndjson`);try{if(_L.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=Oe(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let d=new AbortController,p=setTimeout(()=>d.abort(),OL),h;try{h=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${$L}`,{headers:c,signal:d.signal})}finally{clearTimeout(p)}if(h.status===404)return{restored:!1,reason:"unknown to plane"};if(!h.ok)return{restored:!1,reason:`plane ${h.status}`};let w=await h.json();for(let g of w.events||[])l.push(JSON.stringify(g));if(l.length>RL)return{restored:!1,reason:"event count over safety cap"};if(w.next==null)break;u=w.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await Dh.mkdir(s,{recursive:!0});let f=`${a}.pull-${process.pid}-${Date.now()}`;return await Dh.writeFile(f,l.join(`
|
|
206
206
|
`)+`
|
|
207
|
-
`),await
|
|
207
|
+
`),await Dh.rename(f,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var $L,RL,OL,IA=H(()=>{ct();ge();$L=1e3,RL=5e5,OL=3e4});var aE={};tf(aE,{HANDOFF_LATEST_REL:()=>Po,KNOWN_AGENTS:()=>nc,TRACK_NAME_MAX:()=>Yh,addTrackRepo:()=>dw,adoptTrack:()=>$w,agentAvailability:()=>Vu,announceNodeUpdate:()=>Rn,answerFullscreenOffer:()=>vw,answerPrompt:()=>Yu,archiveTrack:()=>Lo,archiveTrackForMergedPr:()=>Vn,augmentFirstPromptAfterHandoff:()=>jA,buildAgentCmd:()=>Zh,canResumeSubtask:()=>rE,cancelQueued:()=>sc,cancelQueuedToDraft:()=>ow,cancelStopJob:()=>ac,cancelSubtask:()=>sd,changedRepos:()=>Ro,commitTransferOut:()=>rd,createSetupTrack:()=>mw,createTrack:()=>Zu,deleteTrack:()=>ww,demoteTransferredTracks:()=>Iw,ensureLeadEpochs:()=>sa,forceMergeSubtask:()=>Mw,getHandoffJob:()=>la,getSubtaskJob:()=>NF,getTrack:()=>oe,getTransferOutJob:()=>Aw,healDeadLead:()=>ec,interruptLead:()=>Xu,isArchiving:()=>TF,isHandoffNoteEcho:()=>Gh,isInterruptEcho:()=>Kh,isModelSwitchEcho:()=>Vh,leadCliDead:()=>yw,listSubtaskJobs:()=>Uo,listTracks:()=>fe,matchTranscriptsToBoxes:()=>UA,mergeNotifyMessage:()=>QA,navigatePrompt:()=>rw,notifyTrackForMergedPr:()=>zn,notifyTrackForResolvedPr:()=>Qu,offerFollowupsForMergedPr:()=>Fo,pendingOffers:()=>tw,queueShipCompleteNotify:()=>aw,queuedMessages:()=>Do,reapArchivedOrphans:()=>xw,reconcileDemotedTracks:()=>kw,reconcileHandoffOutcome:()=>eE,reconcileInterruptedHandoffs:()=>Tw,reconcileInterruptedSubtasks:()=>Rw,reconcileNodeUpdateNotices:()=>ew,reconcileSubtaskOutcome:()=>nE,refreshStandingInstructions:()=>pw,refuseIfTransferred:()=>_w,rehydrateLead:()=>cc,removeTrackRepo:()=>fw,renameTrack:()=>bw,reposOf:()=>Pt,resolveTrackBox:()=>Ni,resolveTrackBoxByHookToken:()=>oc,restartLead:()=>td,resumeAgentCmd:()=>Qh,sayToLead:()=>Nt,sendOptionsForAgent:()=>gn,setTrackDraft:()=>lw,setTrackModel:()=>uw,startLeadHandoff:()=>nd,startSpinUpView:()=>iw,startSubtask:()=>Cw,startTransferOut:()=>Ew,stopJobActive:()=>qA,subtaskMessages:()=>nw,trackMessages:()=>zu,unarchiveTrack:()=>ed});import Q from"node:fs/promises";import te from"node:path";import{randomUUID as jo}from"node:crypto";import{execFile as ML}from"node:child_process";import{promisify as NL}from"node:util";function Zh(e,{skipPermissions:t=!0}={}){let n=Ku[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 Qh(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Ku[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&&!en(r,t)){let c=o&&Le(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 ca(e){let t=Ku[e];if(t)return process.env[t.envVar]||t.base}async function Vu({maxAgeMs:e=6e4}={}){if(Hr.value&&Date.now()-Hr.at<e)return Hr.value;let t=Hr.value,n=await Promise.all(nc.map(async r=>{let o=await fl(ca(r),{retries:[]});return o===fs?[r,t?.[r]??!0]:[r,o===dl]}));return Hr.value=Object.fromEntries(n),Hr.at=Date.now(),Hr.value}async function rc(e,t=!0){let n=Zh(e,{skipPermissions:t});if(await fl(n)===ds){Hr.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.":Mf[r]?`Install it on this node: ${Mf[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 jA(e,t=Po){return`${e.trim()}
|
|
208
208
|
|
|
209
|
-
(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
|
|
210
|
-
`;return mh=mh.then(()=>z.mkdir(En,{recursive:!0})).then(()=>z.appendFile(Tj,n)).catch(()=>{}),mh}async function An({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let f=null;try{f=JSON.parse(await z.readFile(hh,"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)Ve(f)||!f.leadBox||(Te(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 ee().catch(()=>{}),s==="available")try{await z.mkdir(J.dirname(hh),{recursive:!0}),await z.writeFile(hh,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Rh({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await Z(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||Rj(e,c)||(Te(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ee().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function Rj(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!0;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function tE(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=>Oj.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 nE(e,t,n,{kind:r}={}){let o=await ge(t);if(!o)throw new Error(`box "${t}" is not running`);let s=na(o.agent);await hr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Ze(t,n,{...pn(o.agent,n),kind:r||"orchestration"});let a=await ni({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await re({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await Z(),c=he(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);Te(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await ee()}async function Cj(e){let t=J.join(ts(e),`${e.id}.ndjson`),n;try{n=await z.stat(t)}catch{return null}let r=KT.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await F(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return KT.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Mj(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:sm(n.name,n.agent)}async function Mt(e,t=null){let n=t??new Map((await zt()).map(c=>[c.name,c])),r=!!e.transferredTo,o=e.boxes.map(c=>{let l=r?null:n.get(c.name),u=Fr.has(c.state)?c.state:l?c.role==="lead"?"live":c.state:"stopped";return{...c,state:u,cwd:l?.cwd||c.cwd,agent:l?.agent||c.agent,sessionId:l?.sessionId||c.sessionId}}),[s,{working:a,prompted:i}]=await Promise.all([Cj(e),Mj(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:zi.has(e.id)}}function rE(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-Nj,d=r[u+1]?.createdAt??1/0,p=o.find(h=>!s.has(h.id)&&h.birthMs>=f&&h.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function oE(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=nb(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=rE(u,a,d=>i.has(d));for(let d of f){let p=u.find(h=>h.name===d.box);p&&(p.sessionId=d.sessionId),await Vy(d.box,d.sessionId),n=!0}}return n}async function le(){let e=await Z();await oE(e)&&await ee();let t=new Map((await zt()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Mt(n,t)))}async function Ii(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 Qi(e){let t=va(e);if(!t)return null;let n=await Z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Pg(o.hookTokenHash,t))return{track:r,box:o};return null}async function te(e){let t=await Z(),n=he(t,e);return n?(await oE(t)&&await ee(),Mt(n)):null}async function Oh(e){let t=await F(e).catch(()=>[]),n;try{n=wr(t)}catch{return[]}return dk(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function vh(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(Pj[e.agent]||[]).some(n=>n.test(t))}function Sh(e){let t=(e.text||"").trim();return t?Dj.some(n=>n.test(t)):!1}async function Nu(e,t={}){let n=await te(e);if(!n)return null;let r=new Set([...n.events.filter(A=>A.type==="orchestration-prompt"&&A.prompt).map(A=>String(A.prompt).trim()),...n.events.filter(A=>A.type==="said"&&A.text).map(A=>String(A.text).trim())]),o=ra(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=jm(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[..._S(n),...l],d={windows:[...Nk(n),...Pk(n,Il),...a,...u]},h=(await Promise.all(n.boxes.map(A=>yp(A,t)))).flat(),w=h.map(A=>{let G={kind:"message",...A};if(G.role==="assistant"&&(G.text||"").trim()&&G.tools?.length){let Ge=li(G.agent).cliTools,_t=G.tools.filter(at=>!Ge.has(at));return{...G,tools:_t.length?_t:void 0}}return G}).map(A=>{if(A.role!=="user")return A;let G=El(A.text);return G?G.kind==="suppress"?null:{...A,note:G,text:nk(G)}:A}).filter(Boolean).filter(A=>!(A.role==="user"&&r.has((A.text||"").trim()))).filter(A=>!vh(A)).filter(A=>!Sh(A)).filter(A=>!xh(A)).filter(A=>!ah(A)).filter(A=>A.role!=="assistant"||!!(A.text||"").trim()||!!A.images?.length).filter(A=>!Dk(A,d)),g=w.filter(A=>A.failed&&A.text===ao);if(g.length){let A=new Map;for(let G of g){if(!A.has(G.box)){let at=[...(await Ce(G.box,{lines:Xe}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(nt=>nt[1]);A.set(G.box,at.length?at[at.length-1]:null)}let Ge=A.get(G.box);Ge&&(G.text=`\u25A0 ${Ge}`)}}let k=await Promise.all(n.boxes.map(A=>h0(A,t)));await Qv(n,k.flat());let{agentTurnBoxes:S}=await Xv(n,w);for(let A of n.boxes)S.has(A.name)&&A.cwd&&zc(A.cwd);await ux(n),await NS(n,w).catch(()=>{});for(let A of n.boxes)A.agent==="codex"&&A.state==="live"&&await tS(n,A),A.agent==="cursor"&&A.state==="live"&&await aS(n,A);let E=await Promise.all(n.boxes.map(A=>m0(A,t)));await Zv(n,E.flat().filter(A=>!wh(A.box,A.ts,u)));let R=await F(n),B=c&&i.viewId?i.viewId:null,T=vx(R),_=wr(R).filter(A=>!(A.kind==="message"&&(vh(A)||Sh(A)||xh(A)||ah(A)))&&!(A.kind==="view-card"&&B&&A.viewId===B)),O=Sx(_,T),I=_.filter(A=>!(A.kind==="message"&&A.role==="assistant"&&!O.has(A)&&wh(A.box,A.ts,T))),U=Zf(R,I).map(A=>({kind:"command-batch",id:A.id,ts:A.pending?A.ts:(A.anchorTs??A.ts)-1,anchorTs:A.anchorTs,anchorBox:A.anchorBox,agent:A.agent,boxRole:A.boxRole,commands:A.commands,pending:A.pending})).filter(A=>!wh(A.anchorBox,A.anchorTs??A.ts,u)),H=lk(uk([...I,...U].filter(A=>A.ts!=null).sort((A,G)=>A.ts-G.ts))),ae=jj(n),X=ae.size?H.filter(A=>A.kind==="event"||!ae.has(A.box??A.anchorBox)):H,C=n.boxes.find(A=>A.name===n.leadBox)||null,Q=C?await Ka(C.cwd,C.sessionId,{fallbackNewest:!1}):null;if(!Q&&C){let A=[...R].reverse().find(G=>G.type==="context.usage"&&G.boxId===C.name);A?.payload?.window&&(Q={pct:A.payload.pct??(A.payload.occupancy!=null?A.payload.occupancy/A.payload.window:null),occupancy:A.payload.occupancy??null,window:A.payload.window,windowKnown:!0,model:A.payload.model||null})}let ve=[...h].reverse().find(A=>A.role==="assistant"&&A.model),W=ve?.model||null,be=[...n.events||[]].reverse().find(A=>A.type==="model-change"),se=Q?.model||W;be&&(!ve?.ts||be.at>=ve.ts)&&(se=be.to);let pe=C?await Ce(C.name,{lines:Xe}).catch(()=>""):"",ie=C&&wo(C.agent,pe)?await Nn(C.name).catch(()=>pe):pe,L=C?Tr(C,ie):null;if(L&&C){let A=Ml(C,L,ie);if(A){let G=O0(C.name,L.promptId);G.action!=="give-up"&&(G.action==="send"&&(Le(C.name,A.keys,{kind:"auto-reject"}).catch(()=>{}),G.first&&$(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{ts:Date.now(),promptId:L.promptId,rule:A.id}}).catch(()=>{})),L=null)}}!L&&C&&!ws(C.agent,pe)&&!Mo(n.id).length&&(L=Jf(X,{leadBoxId:C.name,provider:C.agent,isAnswered:A=>R.some(G=>G.type==="prompt.answered"&&G.payload?.promptId===A)})),L&&dx(n,C,L);let ce=X.filter(A=>!(A.kind==="message"&&A.role==="assistant"&&A.ask&&!String(A.text||"").trim()&&!A.images?.length)),Ae=!!C&&su(C.name,C.agent,pe)&&!L;C&&!C.readyAt&&(Ae||L||pe&&gs(C.agent,pe))&&(C.readyAt=Date.now(),await ee());let _e=!!C&&!C.readyAt&&Date.now()-(n.createdAt||0)<Bj;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&C&&gs(C.agent,pe)&&!L){let A=await Z(),G=he(A,n.id);G?.setup&&!G.setup.connectedAt&&(G.setup.connectedAt=Date.now(),n.setup.connectedAt=G.setup.connectedAt,await $(G,{type:"provider.connected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{provider:C.agent,ts:G.setup.connectedAt}}).catch(()=>{}),await ee())}let xt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,je=null;if(c){let A=Date.now()-i.startedAt;if(i.viewId&&!Ae){let G=yu(n.id);G&&(Te(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:G.finishedAt}),await ee())}else if(L||A>ES&&!Ae&&!i.viewId||A>Bm){let G=L?"needs-input":A>Bm?"timeout":"no-view";yu(n.id,{error:G})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:C?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:G}}).catch(()=>{})}else je={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ce,context:Q?{pct:Q.pct,occupancy:Q.occupancy,window:Q.window,windowKnown:Q.windowKnown,model:Q.model}:null,models:{current:se,options:rx(C?.agent)},pendingPrompt:L,working:Ae,stopping:aE(n.id),leadStarting:_e,handoff:xt,view:je,queued:Mo(n.id).filter(A=>A.kind!=="ship-notify"&&A.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function jj(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 Ch(e,t){let n=await te(e);if(!n)return null;let r=jo(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 F(n),a=wr(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Zf(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 Ce(o,{lines:Xe}).catch(()=>"");u=su(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Pu(e,{promptId:t,answers:n,text:r}={}){let o=he(await Z(),e);if(!o)throw new Error(`track "${e}" not found`);tc(e,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await ge(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Ep(s);if(i||(i=await Lj(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let h=typeof r=="string"?r.trim():"";if(!h)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(h)||h.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Ze(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Le(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=ak(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Pt(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=_0(i,n||[]);await Le(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=Rb(i,n||[]);await Ze(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let g=h[w],k=new Set(Array.isArray(n?.[w])?n[w]:[]),S=[...k].map(R=>g.options?.[R]).filter(Boolean).map(R=>({label:R.label,description:R.description||""})),E=(g.options||[]).map((R,B)=>k.has(B)?null:R?.label).filter(Boolean);Hp(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:S,rejected:E})}return await re({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),d=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),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 re({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Hp(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function Lj(e,t){if(!t)return null;let n=await F(e).catch(()=>[]);return Jf(wr(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Mh(e,{promptId:t,questionIndex:n}={}){let r=he(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 ge(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Ep(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=I0(a,n);return c.length?(await Le(r.leadBox,c,{kind:"navigate-prompt"}),await re({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 Du(e){let t=qi.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},qi.set(e,t)),t}function Mo(e){return(qi.get(e)?.items??[]).map(t=>({...t}))}function ec(e){let t=qi.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 Nh(e){let t=ec(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=he(await Z(),e);if(o)return r=Zs(o,n),await ee(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Ph(e){let t=Du(e);t.timer||(t.timer=setInterval(()=>Uj(e).catch(()=>{}),Fj))}async function Uj(e){let t=qi.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=he(await Z(),e);if(!n||n.archivedAt){ec(e);return}let r=ra(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Oo(t,!1);return}let o=await ge(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await zh(o)&&(o=await Yi(n,s,"queue")?await ge(n.leadBox):null,await ee()),!o){Oo(t,!1);return}if(yo(s?.agent)){Oo(t,!1);return}let a=await Ce(n.leadBox,{lines:Xe}).catch(()=>"");if(gl(s?.agent,a)){Oo(t,!1);return}if(wo(s?.agent,a)){let c=await Nn(n.leadBox).catch(()=>a);if(Tr(s,c)){Oo(t,!1);return}}if(!Oo(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await Th(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 Yi(n,s,"queue-gate")}await ee()}finally{t.flushing=!1}}}async function sE(e,t,n,r){let o=yo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Zs(e,[n]),await ee()),V0(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 Hj(e,t){let n=await Ce(e.leadBox,{lines:Xe}).catch(()=>"");if(!wo(t?.agent,n))return!1;let r=await Nn(e.leadBox).catch(()=>n);return!!Tr(t,r)}async function Th(e,t,n,r,o=null,s=null){await sE(e,t,r,o);let a=Ki.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=eE(r,e.pendingHandoffHydrate.artifact||Co),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=BT(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
209
|
+
(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 Gh(e){return e.role==="user"&&BL.test((e.text||"").trim())}async function ne(){return Kt||(Bh||=(async()=>{try{Kt=JSON.parse(await Q.readFile(DA,"utf8"))}catch{Kt={tracks:[]}}finally{Bh=null}return Kt})(),Bh)}async function se(){await Tt(DA,Kt)}function FL(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
210
|
+
`;return jh=jh.then(()=>Q.mkdir($n,{recursive:!0})).then(()=>Q.appendFile(PL,n)).catch(()=>{}),jh}async function Rn({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 Q.readFile(Lh,"utf8"))}catch{}if(f?.latest===t&&f?.current===e)return{announced:0,deduped:!0}}let l=await ne(),u=0;for(let f of l.tracks)Ge(f)||!f.leadBox||(_e(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 se().catch(()=>{}),s==="available")try{await Q.mkdir(te.dirname(Lh),{recursive:!0}),await Q.writeFile(Lh,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function ew({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await ne(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||UL(e,c)||(_e(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await se().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function UL(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!0;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function LA(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=>HL.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 FA(e,t,n,{kind:r}={}){let o=await ke(t);if(!o)throw new Error(`box "${t}" is not running`);let s=ca(o.agent);await br({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await tt(t,n,{...gn(o.agent,n),kind:r||"orchestration"});let a=await ai({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await ue({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await ne(),c=ye(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);_e(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await se()}async function WL(e){let t=te.join(as(e),`${e.id}.ndjson`),n;try{n=await Q.stat(t)}catch{return null}let r=$A.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await W(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return $A.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function qL(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Tm(n.name,n.agent)}async function Ct(e,t=null){let n=t??new Map((await Zt()).map(c=>[c.name,c])),r=!!e.transferredTo,o=e.boxes.map(c=>{let l=r?null:n.get(c.name),u=Wr.has(c.state)?c.state:l?c.role==="lead"?"live":c.state:"stopped";return{...c,state:u,cwd:l?.cwd||c.cwd,agent:l?.agent||c.agent,sessionId:l?.sessionId||c.sessionId}}),[s,{working:a,prompted:i}]=await Promise.all([WL(e),qL(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:Qi.has(e.id)}}function UA(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-GL,d=r[u+1]?.createdAt??1/0,p=o.find(h=>!s.has(h.id)&&h.birthMs>=f&&h.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function HA(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=Cb(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=UA(u,a,d=>i.has(d));for(let d of f){let p=u.find(h=>h.name===d.box);p&&(p.sessionId=d.sessionId),await Sb(d.box,d.sessionId),n=!0}}return n}async function fe(){let e=await ne();await HA(e)&&await se();let t=new Map((await Zt()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Ct(n,t)))}async function Ni(e,t){let n=await ne();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 oc(e){let t=Ea(e);if(!t)return null;let n=await ne();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&dy(o.hookTokenHash,t))return{track:r,box:o};return null}async function oe(e){let t=await ne(),n=ye(t,e);return n?(await HA(t)&&await se(),Ct(n)):null}async function tw(e){let t=await W(e).catch(()=>[]),n;try{n=kr(t)}catch{return[]}return Wk(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function Kh(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(KL[e.agent]||[]).some(n=>n.test(t))}function Vh(e){let t=(e.text||"").trim();return t?VL.some(n=>n.test(t)):!1}async function zu(e,t={}){let n=await oe(e);if(!n)return null;let r=new Set([...n.events.filter(_=>_.type==="orchestration-prompt"&&_.prompt).map(_=>String(_.prompt).trim()),...n.events.filter(_=>_.type==="said"&&_.text).map(_=>String(_.text).trim())]),o=la(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=lh(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...fT(n),...l],d={windows:[...f0(n),...p0(n,Pl),...a,...u]},h=(await Promise.all(n.boxes.map(_=>Bp(_,t)))).flat(),w=h.map(_=>{let N={kind:"message",..._};if(N.role==="assistant"&&(N.text||"").trim()&&N.tools?.length){let xe=pi(N.agent).cliTools,Ne=N.tools.filter(Xe=>!xe.has(Xe));return{...N,tools:Ne.length?Ne:void 0}}return N}).map(_=>{if(_.role!=="user")return _;let N=Ts(_.text);return N?N.kind==="suppress"?null:{..._,note:N,text:Nk(N)}:_}).filter(Boolean).filter(_=>!(_.role==="user"&&r.has((_.text||"").trim()))).filter(_=>!Kh(_)).filter(_=>!Vh(_)).filter(_=>!Gh(_)).filter(_=>!Rh(_)).filter(_=>_.role!=="assistant"||!!(_.text||"").trim()||!!_.images?.length).filter(_=>!m0(_,d)),g=w.filter(_=>_.failed&&_.text===co);if(g.length){let _=new Map;for(let N of g){if(!_.has(N.box)){let Xe=[...(await De(N.box,{lines:et}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(wt=>wt[1]);_.set(N.box,Xe.length?Xe[Xe.length-1]:null)}let xe=_.get(N.box);xe&&(N.text=`\u25A0 ${xe}`)}}let b=await Promise.all(n.boxes.map(_=>V0(_,t)));await zS(n,b.flat());let{agentTurnBoxes:v}=await KS(n,w);for(let _ of n.boxes)v.has(_.name)&&_.cwd&&tl(_.cwd);await Hx(n),await bT(n,w).catch(()=>{});for(let _ of n.boxes)_.agent==="codex"&&_.state==="live"&&await JS(n,_),_.agent==="cursor"&&_.state==="live"&&await tT(n,_);let x=await Promise.all(n.boxes.map(_=>K0(_,t)));await VS(n,x.flat().filter(_=>!Fh(_.box,_.ts,u)));let I=await W(n),P=c&&i.viewId?i.viewId:null,B=ev(I),j=kr(I).filter(_=>!(_.kind==="message"&&(Kh(_)||Vh(_)||Gh(_)||Rh(_)))&&!(_.kind==="view-card"&&P&&_.viewId===P)),L=tv(j,B),A=j.filter(_=>!(_.kind==="message"&&_.role==="assistant"&&!L.has(_)&&Fh(_.box,_.ts,B))),E=gp(I,A).map(_=>({kind:"command-batch",id:_.id,ts:_.pending?_.ts:(_.anchorTs??_.ts)-1,anchorTs:_.anchorTs,anchorBox:_.anchorBox,agent:_.agent,boxRole:_.boxRole,commands:_.commands,pending:_.pending})).filter(_=>!Fh(_.anchorBox,_.anchorTs??_.ts,u)),q=Uk(Hk([...A,...E].filter(_=>_.ts!=null).sort((_,N)=>_.ts-N.ts))),z=YL(n),F=z.size?q.filter(_=>_.kind==="event"||!z.has(_.box??_.anchorBox)):q,R=n.boxes.find(_=>_.name===n.leadBox)||null,G=R?await Ya(R.cwd,R.sessionId,{fallbackNewest:!1}):null;if(!G&&R){let _=[...I].reverse().find(N=>N.type==="context.usage"&&N.boxId===R.name);_?.payload?.window&&(G={pct:_.payload.pct??(_.payload.occupancy!=null?_.payload.occupancy/_.payload.window:null),occupancy:_.payload.occupancy??null,window:_.payload.window,windowKnown:!0,model:_.payload.model||null})}let X=[...h].reverse().find(_=>_.role==="assistant"&&_.model),K=X?.model||null,he=[...n.events||[]].reverse().find(_=>_.type==="model-change"),ce=G?.model||K;he&&(!X?.ts||he.at>=X.ts)&&(ce=he.to);let re=R?await De(R.name,{lines:et}).catch(()=>""):"",Ae=R&&yo(R.agent,re)?await jn(R.name).catch(()=>re):re,U=R?Ir(R,Ae):null;if(U&&R){let _=Fl(R,U,Ae);if(_){let N=lx(R.name,U.promptId);N.action!=="give-up"&&(N.action==="send"&&(He(R.name,_.keys,{kind:"auto-reject"}).catch(()=>{}),N.first&&$(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{ts:Date.now(),promptId:U.promptId,rule:_.id}}).catch(()=>{})),U=null)}}!U&&R&&!xs(R.agent,re)&&!Do(n.id).length&&(U=hp(F,{leadBoxId:R.name,provider:R.agent,isAnswered:_=>I.some(N=>N.type==="prompt.answered"&&N.payload?.promptId===_)})),U&&Wx(n,R,U);let de=F.filter(_=>!(_.kind==="message"&&_.role==="assistant"&&_.ask&&!String(_.text||"").trim()&&!_.images?.length)),Y=!!R&&fu(R.name,R.agent,re)&&!U;R&&!R.readyAt&&(Y||U||re&&vs(R.agent,re))&&(R.readyAt=Date.now(),await se());let Re=!!R&&!R.readyAt&&Date.now()-(n.createdAt||0)<zL;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&R&&vs(R.agent,re)&&!U){let _=await ne(),N=ye(_,n.id);N?.setup&&!N.setup.connectedAt&&(N.setup.connectedAt=Date.now(),n.setup.connectedAt=N.setup.connectedAt,await $(N,{type:"provider.connected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{provider:R.agent,ts:N.setup.connectedAt}}).catch(()=>{}),await se())}let pe=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Ie=null;if(c){let _=Date.now()-i.startedAt;if(i.viewId&&!Y){let N=Ru(n.id);N&&(_e(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:N.finishedAt}),await se())}else if(U||_>uT&&!Y&&!i.viewId||_>ch){let N=U?"needs-input":_>ch?"timeout":"no-view";Ru(n.id,{error:N})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:R?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:N}}).catch(()=>{})}else Ie={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:de,context:G?{pct:G.pct,occupancy:G.occupancy,window:G.window,windowKnown:G.windowKnown,model:G.model}:null,models:{current:ce,options:Px(R?.agent)},pendingPrompt:U,working:Y,stopping:qA(n.id),leadStarting:Re,handoff:pe,view:Ie,queued:Do(n.id).filter(_=>_.kind!=="ship-notify"&&_.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function YL(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 nw(e,t){let n=await oe(e);if(!n)return null;let r=Uo(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 W(n),a=kr(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=gp(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 De(o,{lines:et}).catch(()=>"");u=fu(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Yu(e,{promptId:t,answers:n,text:r}={}){let o=ye(await ne(),e);if(!o)throw new Error(`track "${e}" not found`);ac(e,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await ke(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await qp(s);if(i||(i=await JL(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let h=typeof r=="string"?r.trim():"";if(!h)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(h)||h.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await tt(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await He(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=jk(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Nt(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=sx(i,n||[]);await He(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=ak(i,n||[]);await tt(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let g=h[w],b=new Set(Array.isArray(n?.[w])?n[w]:[]),v=[...b].map(I=>g.options?.[I]).filter(Boolean).map(I=>({label:I.label,description:I.description||""})),x=(g.options||[]).map((I,P)=>b.has(P)?null:I?.label).filter(Boolean);im(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:v,rejected:x})}return await ue({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),d=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),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 ue({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),im(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function JL(e,t){if(!t)return null;let n=await W(e).catch(()=>[]);return hp(kr(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function rw(e,{promptId:t,questionIndex:n}={}){let r=ye(await ne(),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 ke(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await qp(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=ax(a,n);return c.length?(await He(r.leadBox,c,{kind:"navigate-prompt"}),await ue({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 Ju(e){let t=Yi.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Yi.set(e,t)),t}function Do(e){return(Yi.get(e)?.items??[]).map(t=>({...t}))}function sc(e){let t=Yi.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 ow(e){let t=sc(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=ye(await ne(),e);if(o)return r=oa(o,n),await se(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function sw(e){let t=Ju(e);t.timer||(t.timer=setInterval(()=>ZL(e).catch(()=>{}),XL))}async function ZL(e){let t=Yi.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=ye(await ne(),e);if(!n||n.archivedAt){sc(e);return}let r=la(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){No(t,!1);return}let o=await ke(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await yw(o)&&(o=await ec(n,s,"queue")?await ke(n.leadBox):null,await se()),!o){No(t,!1);return}if(ko(s?.agent)){No(t,!1);return}let a=await De(n.leadBox,{lines:et}).catch(()=>"");if(Tl(s?.agent,a)){No(t,!1);return}if(yo(s?.agent,a)){let c=await jn(n.leadBox).catch(()=>a);if(Ir(s,c)){No(t,!1);return}}if(!No(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await zh(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 ec(n,s,"queue-gate")}await se()}finally{t.flushing=!1}}}async function WA(e,t,n,r){let o=ko(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(oa(e,[n]),await se()),Ax(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 QL(e,t){let n=await De(e.leadBox,{lines:et}).catch(()=>"");if(!yo(t?.agent,n))return!1;let r=await jn(e.leadBox).catch(()=>n);return!!Ir(t,r)}async function zh(e,t,n,r,o=null,s=null){await WA(e,t,r,o);let a=Ji.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=jA(r,e.pendingHandoffHydrate.artifact||Po),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=xA(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
211
211
|
|
|
212
|
-
${d}`:d}let f=
|
|
212
|
+
${d}`:d}let f=_e(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||ra(e,null);try{if(await br({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:ca(n.agent),bailOnDialog:!0}),(Ji.get(e.id)||0)!==a){!o&&r.trim()&&(oa(e,[r]),await se());let d=new Error("delivery stopped \u2014 the message was handed back as the draft");throw d.interrupted=!0,d}if(await QL(e,t)){let d=Ju(e.id),p={id:jo(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};d.items.unshift(p),sw(e.id);let h=new Error("lead is blocked on a prompt \u2014 message queued");throw h.requeued={id:p.id,position:d.items.length},h}await tt(e.leadBox,i,{...gn(t?.agent,i),kind:c?"lead-handoff-first-prompt":"say"})}catch(d){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),_e(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 pF(e,t,i)||(await GA(e.leadBox,t?.agent,i.length+8,i).catch(()=>{}),_e(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,reason:"unsubmitted"}),o||oa(e,[r])),c&&await ue({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function aw(e,{actions:t}={}){Ju(e).items.push({id:jo(),text:nv(t),ts:Date.now(),kind:"ship-notify"}),sw(e)}async function Nt(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"||!Rs(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=ye(await ne(),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`);_w(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=Rs(t);await WA(s,a,i,n),ac(e,"say");let c=await ke(s.leadBox);if(c&&await yw(c)?await ec(s,a,"say")?(await se(),c=await ke(s.leadBox)):c=null:!c&&await cc(s)&&(await se(),c=await ke(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Ju(e),u=await De(s.leadBox,{lines:et}).catch(()=>""),f=l.items.length>0||Tl(a?.agent,u);if(!f&&yo(a?.agent,u)){let p=await jn(s.leadBox).catch(()=>u);f=!!Ir(a,p)}if(f){let p={id:jo(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),sw(e),n||ra(s,null),await se(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let d;try{d=await zh(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await ec(s,a,"say-gate")||(await se(),c=await ke(s.leadBox),!c))throw p;d=await zh(s,a,c,i,n,o)}return await se(),d?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:d.position}:{ok:!0,leadBox:s.leadBox}}async function iw(e,{hint:t}={}){let n=ye(await ne(),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=lh(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";dT(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${RA}
|
|
213
213
|
|
|
214
|
-
What to preview: ${String(t).trim()}`:
|
|
214
|
+
What to preview: ${String(t).trim()}`:RA;try{await Nt(e,a,{kind:"spin-up-view"})}catch(i){throw Ru(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Xu(e){let t=ye(await ne(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);Ji.set(e,(Ji.get(e)||0)+1);let n=sc(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=na.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=oa(t,r),await se()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=sF(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Bo(eF)]);let a=ye(await ne(),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 qA(e){let t=na.get(e);return!!t&&!t.canceled}function ac(e,t){let n=na.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function cw(e){ac(e,"track-ended"),Ji.delete(e),sc(e)}function sF(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}),na.set(e,r),(async()=>{let s=ye(await ne(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await ke(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<tF&&!r.canceled;g++){await He(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let b of nF){await Bo(b);let v=await De(s.leadBox,{lines:et}).catch(()=>"");if(!xs(a?.agent,v)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<oF&&!r.canceled&&(await Bo(rF),!r.canceled);g++){let b=await De(s.leadBox,{lines:et}).catch(()=>"");if(!xs(a?.agent,b)){c=!0;break}await He(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=ye(await ne(),e)||s,f=u?.boxes.find(g=>g.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await lF(u,f).catch(()=>null),d||(p=await fF(u,f).catch(()=>null)));let h=u&&i&&!r.canceled?await iF(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=oa(u,w),await se()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await aF(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:!!h,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await ue({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:!!h}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),na.get(e)===r&&na.delete(e),o()}),r}async function aF(e,t,n){await $(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function iF(e){let t=Nu(await W(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 lF(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 cF){await Bo(r);let o=await De(e.leadBox,{lines:et}).catch(()=>""),s=oi(t.agent,o);if(s&&Al(n.text,s))return await GA(e.leadBox,t.agent,n.text.length+8),_e(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function fF(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>uF||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ve(r.text))return null;for(let s of dF){await Bo(s);let a;try{a=await Bp(t,{})}catch{return null}if(hk(a,r))return null;let i=await De(e.leadBox,{lines:et}).catch(()=>"");if(oi(t.agent,i))return null}return _e(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function pF(e,t,n){return ai({sessionName:e.leadBox,agent:t?.agent,agentCmd:ca(t?.agent),text:n,trackId:e.id,flow:"say"})}async function GA(e,t,n,r=null){for(let o=0;o<3;o++){await ms(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await Bo(200);let s=await De(e,{lines:et}).catch(()=>""),a=oi(t,s);if(!a||r&&!Al(r,a)&&!(t==="codex"&&ap(r,a)))return!0;n=a.length+8}return!1}function ra(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 oa(e,t){let n=[],r=o=>n.some(s=>Ve(s).includes(Ve(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?ra(e,n.join(`
|
|
215
215
|
|
|
216
|
-
`),{source:"restore"})?.text??null:null}function
|
|
216
|
+
`),{source:"restore"})?.text??null:null}function hF(e,t){let n=Ve(t);if(!n)return!1;if(Do(e.id).some(o=>Ve(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>mF)break;if(!(s.type!=="said"||!s.text||Ve(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function lw(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=ye(await ne(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=Rs(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&hF(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;Ve(o).includes(Ve(i))?ra(r,o):ra(r,`${i}
|
|
217
217
|
|
|
218
|
-
${o}`,{source:"restore"})}else
|
|
219
|
-
`);return`${
|
|
218
|
+
${o}`,{source:"restore"})}else ra(r,o);return await se(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function uw(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=ye(await ne(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await ke(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=Dx(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Ya(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await br({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ca(o.agent)});let u=await Bx(a,r.leadBox,i);return _e(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await se(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function KA(e){for(let t of e){if(!t||!await Os(t))continue;let n=await px(t).catch(()=>null);if(!n)continue;if((await Q.stat(te.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Pt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:te.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function Ro(e){let t=[];for(let n of Pt(e?.workspace)){if(!n?.dir)continue;let r=await mi(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await hi(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function VA(e){let t=e;typeof t=="string"&&!te.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await rs(t)??await Vc(t));let n=await KA([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 wF(e,t,n,r){let o=`track/${Ow(t)}-${e}`,s=[];try{await Q.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await VA(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Ee(c).catch(()=>null),u=te.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${te.basename(c)}-${p}`;a.add(u);let f=te.join(n,u),d=await Ns(c);await Ms(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await ic(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await $t(i.base,i.dir).catch(()=>{});throw await Q.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function dw(e,t){let n=await ne(),r=ye(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 VA(typeof t=="string"?t.trim():t);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Ee(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=te.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${te.basename(s)}-${d}`;let l=te.join(o.dir,c),u=await Ns(s);await mx(s,o.branch)?await Ul(s,l,o.branch):await Ms(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 ic(o.dir,o.repos,o.branch),_e(r,"retagged",{action:"add",repo:a??s,name:c,workspace:zA(o)}),await se(),f}async function fw(e,t){let n=await ne(),r=ye(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 Mt.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 mi(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await hi(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 $t(s.base,s.dir).catch(()=>{}),await Q.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await ic(o.dir,o.repos,o.branch),_e(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:zA(o)}),await se(),{removed:s.name}}async function gF(e,t,{repo:n,repos:r,cwd:o}={}){await Q.mkdir(Vi,{recursive:!0});let s=te.join(Vi,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return wF(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!te.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await rs(n)??await Vc(n));let i=await KA([n,o,DL]);if(i){let c=`track/${Ow(t)}-${e}`;try{let l=await Ns(i);await Ms(i,s,c,l),await Zi(s);let u=await Ee(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await Q.mkdir(s,{recursive:!0}),await Xp(s),await Zi(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Zi(e){try{await Q.writeFile(te.join(e,"AGENTS.md"),Ki,{flag:"wx"})}catch{return}await xF(e,["/AGENTS.md"])}function kF(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
219
|
+
`);return`${bF}
|
|
220
220
|
|
|
221
221
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
222
222
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -228,20 +228,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
228
228
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
229
229
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
230
230
|
or preview an app, run it from that app's own repo directory.
|
|
231
|
-
`}async function
|
|
232
|
-
`).includes(s));o.length&&await
|
|
231
|
+
`}async function ic(e,t,n){let r=kF(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await Q.writeFile(te.join(e,o),r).catch(()=>{})}async function JA(e){let{stdout:t}=await PA("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return te.resolve(e,t.trim())}async function xF(e,t){try{let n=await JA(e);await Q.mkdir(te.dirname(n),{recursive:!0});let r=await Q.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
232
|
+
`).includes(s));o.length&&await Q.appendFile(n,(r.endsWith(`
|
|
233
233
|
`)||!r?"":`
|
|
234
234
|
`)+o.join(`
|
|
235
235
|
`)+`
|
|
236
|
-
`)}catch{}}async function
|
|
236
|
+
`)}catch{}}async function vF(e,t){try{let n=await JA(e),r=await Q.readFile(n,"utf8"),o=r.split(`
|
|
237
237
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
238
|
-
`).length&&await
|
|
239
|
-
`))}catch{}}async function
|
|
238
|
+
`).length&&await Q.writeFile(n,o.join(`
|
|
239
|
+
`))}catch{}}async function pw(){let e={refreshed:0,removed:0,codexGlobal:null};try{await Q.mkdir($n,{recursive:!0}),await Q.writeFile(te.join($n,"CLAUDE.md"),yF).catch(()=>{}),await Q.writeFile(te.join($n,"AGENTS.md"),Ki).catch(()=>{});let t=process.env.CODEX_HOME||te.join(process.env.HOME||"/tmp",".codex");if((await Q.stat(t).catch(()=>null))?.isDirectory()){let r=te.join(t,"AGENTS.md"),o=await Q.readFile(r,"utf8").catch(()=>null);o===null?(await Q.writeFile(r,Hh,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):Uh(o)&&o!==Hh&&(await Q.writeFile(r,Hh).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Vi,ia]){let o=await Q.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=te.join(r,s.name),i=te.join(a,"AGENTS.md"),c=await Q.readFile(i,"utf8").catch(()=>null);c!==null&&Uh(c)&&c!==Ki&&(await Q.writeFile(i,Ki).catch(()=>{}),e.refreshed++);let l=te.join(a,"CLAUDE.md"),u=await Q.readFile(l,"utf8").catch(()=>null);u!==null&&Uh(u)&&(await Q.rm(l,{force:!0}).catch(()=>{}),await vF(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function XA(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Pt(t))n.base&&n.dir&&await $t(n.base,n.dir).catch(()=>{});await Q.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await $t(t.base,t.dir):await Q.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function Zu({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 rc(o,s),i=await ne(),c=jo().slice(0,8),l=await gF(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(h=>(h.boxes||[]).map(w=>w.name))),f=`${e.trim()} lead`;u.has(pl(f))&&(f=`${e.trim().slice(0,50).trim()} ${c} lead`);let d=await gr(f,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0}),p={id:c,name:e.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Sw(d,"lead","live")],events:[],createdAt:Date.now()};return _e(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:h,base:w,baseRef:g,repo:b})=>({name:h,base:w,baseRef:g,repo:b}))}:{}}}),i.tracks.push(p),await se(),ti({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a,onReady:async()=>{let h=p.boxes.find(w=>w.name===p.leadBox);!h||h.readyAt||(h.readyAt=Date.now(),await se())}}),Ct(p)}async function mw({provider:e="claude",fresh:t=!1}={}){if(!SF.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await ne()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Ct(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await Lo(r.id).catch(()=>{})}let o=(await rc(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=jo().slice(0,8),a=te.join($n,"setup",s);await Q.mkdir(a,{recursive:!0});let i=await gr(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&ti({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:[Sw(i,"lead","live")],events:[],createdAt:Date.now()};return _e(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 ne()).tracks.push(c),await se(),Ct(c)}async function hw(e,t,n){await bf(t.cwd).catch(()=>{}),await yT(e).catch(()=>{});let r=aa.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),aa.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of Uo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Nw(a).catch(()=>{}),Mt.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 ne(),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 Qt(a.name).catch(()=>{})}async function ww(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await ne(),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 LT(s),cw(e),await hw(e,s,"track deleted"),n?await wA(e).catch(()=>{}):await hA(e).catch(()=>{}),await Fx(),await XA(s),t&&await FT(e).catch(()=>{}),t?await rA(e).catch(()=>{}):await nA(s).catch(()=>{}),_e(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await se(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Lo(e){let t=await ne(),n=ye(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(),cw(e),_e(n,"archived",{boxes:n.boxes.length}),await se(),n.kind==="setup"?(await ZA(e),Ct(n)):(Qi.add(e),EF(e).catch(()=>{}).finally(()=>Qi.delete(e)),{...await Ct(n),archiving:!0})}async function EF(e){try{await _F(e)}catch{}await ZA(e).catch(()=>{})}async function ZA(e){let t=await ne(),n=ye(t,e);if(!(!n||!n.archivedAt)){await hw(e,n,"track archived");for(let r of n.boxes)Wr.has(r.state)||(r.state="stopped");await se()}}async function _F(e){let t=await ne(),n=ye(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 ke(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await u0(n.leadBox,i.cwd,{timeoutMs:AF})),s="lead-cli"}catch{}if(r||(r=A0(n,await W(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 se()}async function Vn(e,t){let n=await ot(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await gw(n);if(r.length){for(let o of r)await vo(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Lo(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function gw(e){return(await So().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 zn(e,t){return Qu(e,t,"merged")}async function Fo(e,t){let n=await ot(e,t).catch(()=>null);if(!n?.trackId)return null;let r={repo:n.repo??null,number:n.number,title:n.title??null,url:n.url??null},o=await gw(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await pv(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=ye(await ne(),n.trackId);if(!a)return null;let i=s[0];return Ci(await Ct(a),"pr.merged",{repo:n.repo??a.workspace?.repo??null,prs:s,prNumber:i?.number??null,prTitle:i?.title??null,prUrl:i?.url??null}).catch(()=>null)}async function Qu(e,t,n="merged"){let r=await ot(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 gw(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await fv(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await Nt(r.trackId,QA(a),{kind:"merge-notify"}),!0}catch{return!1}}function QA(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 ${OA[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 ${OA[i.outcome]??i.outcome}`).join(`
|
|
240
240
|
`),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}
|
|
241
241
|
${s}
|
|
242
|
-
${a}`}async function zh(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Wa()?!1:Mn(e.name)}async function Yi(e,t,n){return!t||e.archivedAt||Fr.has(t.state)?!1:(await re({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 Yt(t.name).catch(()=>{}),t.state="stopped",rc(e))}async function rc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=bh.get(r.name);if(o)return o;let s=gL(e,r,{freshSession:t,reason:n}).finally(()=>bh.delete(r.name));return bh.set(r.name,s),s}async function gL(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Fr.has(t.state)||await ge(t.name))return!1;let o=n===!0,s=o?Po():null,a=o?Ua(Ih(t.agent),s,t.name):$h(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await sl(a);if(i===as)return Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===is&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(Qs(t)))}:null,l=Date.now();if(o){let u=Qs(t),f=u[u.length-1];f.endedAt==null&&(f.endedAt=l),f.endReason==null&&(f.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=await pr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=va(u.hookToken),Xa({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&Te(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),Te(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Fu(e,{freshSession:t=!1}={}){let n=await Z(),r=he(n,e);if(!r)throw new Error(`track "${e}" not found`);if(!r.archivedAt)throw new Error(`track "${e}" is not archived`);return delete r.archivedAt,zi.delete(e),Te(r,"unarchived",{}),await rc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ee(),Mt(r)}async function Yh(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Eh)throw new Error(`name too long (max ${Eh} chars)`);let r=await Z(),o=he(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 Mt(o);let s=o.name;return o.name=n,Te(o,"renamed",{from:s,to:n}),await ee(),Mt(o)}async function Jh(){let e=await Z(),t=0;for(let n of e.tracks||[])n.transferredTo&&Dl(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ee(),t}async function Xh(){let e=await Z(),t=new Set((await zt()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ve(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ve(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Yt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Te(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Fr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ee(),r}async function Uu(e,{freshSession:t=!1}={}){let n=await Z(),r=he(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);rw(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Fr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ge(r.leadBox)){let i=await Ce(r.leadBox,{lines:Xe}).catch(()=>"");if(gl(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Yt(r.leadBox),o.state="stopped"}let a=await rc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ee(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Mt(r)}async function Zh(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 F(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 Uu(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 zT(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return hu({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function Qs(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 Ou(e,t){let n=await Z(),r=he(n,e);r&&(r.handoff={...r.handoff||{},...t},await ee())}function wE(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 ew(){let e=await Z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=wE(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ge(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=Qs(l),d=f[f.length-1],p=d.agent,h=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Co,at:u},Te(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await re({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 re({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Yt(i).catch(()=>{}),a&&await Yt(a).catch(()=>{})}return t&&await ee(),t}async function Hu(e,t,n=!0,r=null){let o=ea.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=he(await Z(),e);if(!s)throw new Error(`track "${e}" not found`);tc(e,"handoff");let a=await Zi(t,n),i=yo(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 ge(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:tE(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ea.set(e,l),await Ou(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),yL(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 Ou(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),re({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),re({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function yL(e,t,n,r,o){let s=async(B,T={})=>{e.step=B,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${B}`),await Ou(e.trackId,{step:B,toBox:e.toBox}),await re({flow:"lead-handoff",phase:B,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:T.sessionName,...T})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await Z(),i=he(a,e.trackId)?.boxes.find(B=>B.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",yo(c))e.checkpoint="skipped-capped";else try{await tp(e.fromBox,{timeoutMs:QT})}catch(B){e.checkpoint="reconstructed",await re({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:B?.message||String(B)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await np(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 lp(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=Db(d.launchCmd);await hr({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:na(o),launchEcho:p}).catch(B=>{if(B?.code!=="cli-not-running")throw B});let h=Bf(await Ce(e.fromBox,{lines:Xe}).catch(()=>""),p);if(!gs(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let B=$h(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await lp(e.fromBox,{agentCmd:B,agent:c,resumeSessionId:l,pinned:u});let T=await Z(),_=he(T,e.trackId);if(_){let O=_.boxes.find(I=>I.name===e.fromBox);O&&(O.state="live"),Te(_,"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 ee()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await Z(),g=he(w,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let k=g.boxes.find(B=>B.name===e.fromBox);if(!k)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let S=Date.now(),E=Qs(k),R=E[E.length-1];R.endedAt=S,e.reason&&(R.endReason=e.reason),E.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:S,endedAt:null}),k.agent=o,k.sessionId=d.sessionId,k.pinned=d.pinned,k.state="live",k.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||Co,at:S},Te(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:E.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ee(),await s("done",{sessionName:e.fromBox}),await Ou(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function nw(e){let t=_h.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await Z(),r=he(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=ea.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Nt.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 _h.set(e,s),bL(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 bL(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ge(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&yo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await tp(t.leadBox,{timeoutMs:QT})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await np(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 Dt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await nn(u.dir,["push","origin",o],Ah);let{stdout:d}=await nn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
243
|
-
`)[0]}s.push(f)}e.step="wip";let a=await
|
|
244
|
-
`)[0]}else l?.sha&&(I="wip snapshots cover single-repo worktrees only");await WT({id:e,cwd:E}).catch(()=>({restored:!1}));let H=J.join(E,Co),ae=null;(await Zc(E)).restored?ae="checkpoint":typeof u=="string"&&u.trim()&&(await z.mkdir(J.dirname(H),{recursive:!0}),await z.writeFile(H,u),ae="brief");let C=new Set(k.tracks.flatMap(L=>(L.boxes||[]).map(ce=>ce.name))),Q=null;p&&!C.has(p)&&!await ge(p).catch(()=>null)?Q=p:(Q=`${t.trim()} lead`,C.has(al(Q))&&(Q=`${t.trim().slice(0,50).trim()} ${e} lead`));let ve=await pr(Q,E,{agentCmd:g,agent:n});Xa({cwd:ve.cwd,sessionName:ve.name,agent:n,agentCmd:g});let W=Date.now(),be=Qh(ve,"lead","live"),se=(Array.isArray(f)?f:[]).filter(L=>L&&typeof L=="object").map(L=>({...L})),pe=se[se.length-1];pe&&pe.endedAt==null&&(pe.endedAt=W,pe.endReason==null&&(pe.endReason=o==="recover"?"node-offline":"node-transfer")),se.push({agent:n,sessionId:ve.sessionId??null,pinned:ve.pinned===!0,startedAt:W,endedAt:null}),be.epochs=se;let ie={id:e,name:t.trim(),cwd:ve.cwd,workspace:R,leadBox:ve.name,boxes:[be],events:[],createdAt:Number.isFinite(d)?d:W,...ae?{pendingHandoffHydrate:{artifact:Co,at:W}}:{}};return Te(ie,"transferred-in",{box:ve.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(w)?w:null}:{},branch:R.branch??null,wip:O,...U?{wipAdvanced:U}:{},epoch:se.length-1,name:ie.name,cwd:ie.cwd,workspace:{base:R.base??null,branch:R.branch??null,kind:R.kind,...R.baseRef?{baseRef:R.baseRef}:{},...R.repo?{repo:R.repo}:{},...R.repos?{repos:R.repos.map(({name:L,base:ce,baseRef:ue,repo:Ae})=>({name:L,base:ce,baseRef:ue,repo:Ae}))}:{}}}),k.tracks.push(ie),await ee(),{...await Mt(ie),adopt:{wipApplied:O,wipSkipped:I,...U?{wipAdvanced:U}:{},codeless:B,artifactSource:ae}}}catch(_){for(let O of T)await Rt(O.base,O.dir).catch(()=>{});throw await z.rm(E,{recursive:!0,force:!0}).catch(()=>{}),_}}async function gE(e,t,n){let r=await Z(),o=he(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ee())}async function Ys(e,t){e.step=t,await oc(e)}function yE(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 bE(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function TL(e,t,n){if(!bE(n)||Nt.has(t)||!await ge(n.box).catch(()=>null))return!1;try{await z.access(n.dir)}catch{return!1}let o=await ge(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Nt.set(t,s),xE(s,o).catch(a=>kE(s,a)),!0}async function aw(){let e=await Z(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Nt.has(r))continue;if(await TL(n,r,o)){t=!0,Te(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await re({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=yE(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Fr.has(i.state)&&(i.state="canceled"),Te(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 re({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await JT(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Fr.has(r.state)||Nt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?J.join(ta,`${n.id}-${r.subtaskId}`):null;await re({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await JT(n,r.name,o)}}return t=await EL(e)||t,t&&await ee(),t}async function JT(e,t,n){if(t&&await Yt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Dt(e.workspace)){let s=J.join(n,o.name);await Rt(o.base,s).then(()=>!0,()=>!1)||await Rt(o.dir,s).catch(()=>{})}await z.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ge(e.leadBox).catch(()=>null);r?.cwd&&await Rt(r.cwd,n).catch(()=>{})}async function EL(e){let t;try{t=await z.readdir(ta)}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 Nt.values())o.dir&&n.add(J.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=J.join(ta,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Dt(a.workspace))await Rt(i.base,J.join(s,i.name)).catch(()=>{});await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ge(a.leadBox).catch(()=>null):null;i?.cwd?await Rt(i.cwd,s).catch(()=>{}):await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await re({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function cw(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Ts(n))throw new Error("task (string) required");let s=await Zi(t,r),a=yo(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=he(await Z(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ge(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=Dt(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let h of p)if(!h?.dir||!await Es(h.dir))throw new Error(`tagged checkout "${h?.name??"?"}" is missing from the lead workspace (${h?.dir??"no dir"})`);u=p.map(h=>({name:h.name,leadDir:h.dir,base:null}))}else if(!await Es(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Po().slice(0,8),d={id:f,trackId:e,trackName:i.name,agent:t,task:Ts(n),skipPermissions:r,reason:tE(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${iw(i.name)}-${f}`,base:null,repos:u,dir:J.join(ta,`${e}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Nt.set(f,d),await oc(d),AL(d,c,s).catch(p=>kE(d,p)),d}async function kE(e,t){e.failedStep=e.step,e.error=t instanceof Ji?"canceled":t?.message||String(t),e.step=t instanceof Ji?"canceled":"error",e.finishedAt=Date.now(),await uw(e).catch(()=>{}),await oc(e).catch(()=>{}),await RL(e).catch(()=>{})}function Ku(e){let t=Nt.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 lw(e){let t=Nt.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 AL(e,t,n){if(await Ys(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await As(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await oc(e)}else e.base=await As(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Ys(e,"spawn"),await z.mkdir(ta,{recursive:!0}),e.repos){await z.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await _s(a.leadDir,J.join(e.dir,a.name),e.branch,a.base);let s=Dt((he(await Z(),e.trackId)||{}).workspace);await nc(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 _s(t.cwd,e.dir,e.branch,e.base),await Vi(e.dir);await _L(e,t);let r=e.trackName.slice(0,38).trim(),o=await pr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await IL(e,o),await nE(e.trackId,o.name,_j(e.task,!!e.repos),{kind:"subtask"}),await xE(e,t)}async function xE(e,t){await Ys(e,"working");let n=J.join(e.dir,Ru);if(await CL(n,e),e.canceled)throw new Ji;if(await Ys(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=J.join(e.dir,l.name);await Op(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:zT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Np(l.leadDir,l.base,e.branch,zs),l.files=await Pp(l.leadDir,l.base,e.branch,zs);let f=await Dp(l.leadDir,l.base,e.branch,zs);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=>J.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:J.join(l.name,d))),f.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Op(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:zT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Np(t.cwd,e.base,e.branch,zs),e.files=await Pp(t.cwd,e.base,e.branch,zs);let s=await Dp(t.cwd,e.base,e.branch,zs);e.applied=s.applied,e.conflicts=s.conflicts}await oc(e);let r=await OL(e,t,n);await Ys(e,"resume"),await nE(e.trackId,e.leadBox,Ij(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Ys(e,"reap"),await uw(e),e.step="done";let o=Date.now();await gE(e.trackId,e.id,{...qu(e),finishedAt:o}),await $L(e,o),e.finishedAt=o}async function _L(e,t){let n=J.join(e.dir,J.dirname(Wi));await z.mkdir(n,{recursive:!0});let r="";try{r=await bb(t.cwd,t.sessionId)}catch{}if(!r)try{r=await z.readFile(J.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}
|
|
242
|
+
${a}`}async function yw(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Va()?!1:Bn(e.name)}async function ec(e,t,n){return!t||e.archivedAt||Wr.has(t.state)?!1:(await ue({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 Qt(t.name).catch(()=>{}),t.state="stopped",cc(e))}async function cc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=Wh.get(r.name);if(o)return o;let s=IF(e,r,{freshSession:t,reason:n}).finally(()=>Wh.delete(r.name));return Wh.set(r.name,s),s}async function IF(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Wr.has(t.state)||await ke(t.name))return!1;let o=n===!0,s=o?jo():null,a=o?Ga(Zh(t.agent),s,t.name):Qh(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await fl(a);if(i===ds)return _e(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===fs&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(sa(t)))}:null,l=Date.now();if(o){let u=sa(t),f=u[u.length-1];f.endedAt==null&&(f.endedAt=l),f.endReason==null&&(f.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=await gr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ea(u.hookToken),ti({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&_e(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),_e(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),_e(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function ed(e,{freshSession:t=!1}={}){let n=await ne(),r=ye(n,e);if(!r)throw new Error(`track "${e}" not found`);if(!r.archivedAt)throw new Error(`track "${e}" is not archived`);return delete r.archivedAt,Qi.delete(e),_e(r,"unarchived",{}),await cc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await se(),Ct(r)}async function bw(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Yh)throw new Error(`name too long (max ${Yh} chars)`);let r=await ne(),o=ye(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 Ct(o);let s=o.name;return o.name=n,_e(o,"renamed",{from:s,to:n}),await se(),Ct(o)}async function kw(){let e=await ne(),t=0;for(let n of e.tracks||[])n.transferredTo&&Wl(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await se(),t}async function xw(){let e=await ne(),t=new Set((await Zt()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ge(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ge(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Qt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):_e(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Wr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await se(),r}async function td(e,{freshSession:t=!1}={}){let n=await ne(),r=ye(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);_w(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Wr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ke(r.leadBox)){let i=await De(r.leadBox,{lines:et}).catch(()=>"");if(Tl(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Qt(r.leadBox),o.state="stopped"}let a=await cc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await se(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ct(r)}async function vw(e,t,n){let r=await oe(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await W(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 td(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 CA(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return gu({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function sa(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 Gu(e,t){let n=await ne(),r=ye(n,e);r&&(r.handoff={...r.handoff||{},...t},await se())}function eE(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 Tw(){let e=await ne(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=eE(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ke(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=sa(l),d=f[f.length-1],p=d.agent,h=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Po,at:u},_e(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await ue({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 ue({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Qt(i).catch(()=>{}),a&&await Qt(a).catch(()=>{})}return t&&await se(),t}async function nd(e,t,n=!0,r=null){let o=aa.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=ye(await ne(),e);if(!s)throw new Error(`track "${e}" not found`);ac(e,"handoff");let a=await rc(t,n),i=ko(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 ke(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:LA(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return aa.set(e,l),await Gu(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),$F(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 Gu(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ue({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ue({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function $F(e,t,n,r,o){let s=async(P,B={})=>{e.step=P,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${P}`),await Gu(e.trackId,{step:P,toBox:e.toBox}),await ue({flow:"lead-handoff",phase:P,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:B.sessionName,...B})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await ne(),i=ye(a,e.trackId)?.boxes.find(P=>P.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",ko(c))e.checkpoint="skipped-capped";else try{await kp(e.fromBox,{timeoutMs:BA})}catch(P){e.checkpoint="reconstructed",await ue({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:P?.message||String(P)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await xp(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 Ip(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=fk(d.launchCmd);await br({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:ca(o),launchEcho:p}).catch(P=>{if(P?.code!=="cli-not-running")throw P});let h=rp(await De(e.fromBox,{lines:et}).catch(()=>""),p);if(!vs(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let P=Qh(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Ip(e.fromBox,{agentCmd:P,agent:c,resumeSessionId:l,pinned:u});let B=await ne(),j=ye(B,e.trackId);if(j){let L=j.boxes.find(A=>A.name===e.fromBox);L&&(L.state="live"),_e(j,"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 se()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await ne(),g=ye(w,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let b=g.boxes.find(P=>P.name===e.fromBox);if(!b)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let v=Date.now(),x=sa(b),I=x[x.length-1];I.endedAt=v,e.reason&&(I.endReason=e.reason),x.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:v,endedAt:null}),b.agent=o,b.sessionId=d.sessionId,b.pinned=d.pinned,b.state="live",b.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||Po,at:v},_e(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:x.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await se(),await s("done",{sessionName:e.fromBox}),await Gu(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Ew(e){let t=Xh.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await ne(),r=ye(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=aa.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Mt.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 Xh.set(e,s),RF(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 RF(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ke(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&ko(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await kp(t.leadBox,{timeoutMs:BA})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await xp(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 Pt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await an(u.dir,["push","origin",o],Jh);let{stdout:d}=await an(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
243
|
+
`)[0]}s.push(f)}e.step="wip";let a=await Mh({id:t.id,cwd:t.cwd},{force:!0}),i=await ne(),c=ye(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&sa(l),await se(),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 _w(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 rd(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await ne(),o=ye(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};cw(e),await hw(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Wl(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Nh({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,_e(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await se(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function Iw(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await rd(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 MA(e,t,n){try{await Ul(e,t,n)}catch{await an(e,["worktree","prune"]).catch(()=>{}),await Ul(e,t,n)}return t}async function $w(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(qh.has(t))throw new Error("an adopt is already running for this Track");qh.set(t,{startedAt:Date.now()});try{return await OF({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{qh.delete(t)}}async function OF({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:h=null,downSince:w=null}){let g=await rc(n,r),b=await ne(),v=ye(b,e);if(v&&!v.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);v&&(await Nh({id:v.id,cwd:v.cwd}).catch(()=>{}),await XA(v),b.tracks.splice(b.tracks.indexOf(v),1),await se()),await Q.mkdir(Vi,{recursive:!0});let x=te.join(Vi,e);await Q.rm(x,{recursive:!0,force:!0}).catch(()=>{});let I=null,P=!1,B=[];try{let j=[];if(i)for(let U of Array.isArray(c)?c:[]){let de=typeof U=="string"?U:U?.repo;if(!de||!/^[^/\s]+\/[^/\s]+$/.test(de))continue;let ae=await rs(de)??await Vc(de);await an(ae,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Jh),j.push({slug:de,base:ae,name:typeof U=="object"?U?.name??null:null})}if(i&&j.length===1){let{base:U,slug:de}=j[0];await MA(U,x,i),B.push({base:U,dir:x}),await Zi(x);let ae=await Ns(U);I={dir:x,base:U,branch:i,kind:"worktree",baseRef:ae,repo:de}}else if(i&&j.length>=2){await Q.mkdir(x,{recursive:!0});let U=new Set([".bostrat"]),de=[];for(let ae of j){let Y=ae.name||te.basename(ae.base);U.has(Y)&&(Y=ae.slug.replace("/","-"));for(let Ie=2;U.has(Y);Ie++)Y=`${te.basename(ae.base)}-${Ie}`;U.add(Y);let Re=te.join(x,Y);await MA(ae.base,Re,i),B.push({base:ae.base,dir:Re});let pe=await Ns(ae.base);de.push({name:Y,dir:Re,base:ae.base,baseRef:pe,repo:ae.slug})}await ic(x,de,i),I={dir:x,kind:"multi",branch:i,repos:de}}else P=!0,await Q.mkdir(x,{recursive:!0}),await Xp(x),await Zi(x),I={dir:x,base:null,branch:null,kind:"repo"};let L=!1,A=null,E=null;if(l?.sha&&I.kind==="worktree")try{await an(x,["fetch","origin",l.ref||Ch(e)],Jh);let{stdout:U}=await an(x,["rev-parse","FETCH_HEAD^"]),{stdout:de}=await an(x,["rev-parse","HEAD"]),ae=U.trim()===de.trim();ae||await an(x,["merge-base","--is-ancestor",de.trim(),U.trim()]).then(()=>!0,()=>!1)&&(await an(x,["reset","--hard",U.trim()]),E={from:de.trim(),to:U.trim()},ae=!0),ae?(await an(x,["read-tree","--reset","-u","FETCH_HEAD"]),await an(x,["reset","-q"]),L=!0):A="snapshot does not parent the fetched branch head"}catch(U){A=String(U?.message||U).split(`
|
|
244
|
+
`)[0]}else l?.sha&&(A="wip snapshots cover single-repo worktrees only");await _A({id:e,cwd:x}).catch(()=>({restored:!1}));let q=te.join(x,Po),z=null;(await sl(x)).restored?z="checkpoint":typeof u=="string"&&u.trim()&&(await Q.mkdir(te.dirname(q),{recursive:!0}),await Q.writeFile(q,u),z="brief");let R=new Set(b.tracks.flatMap(U=>(U.boxes||[]).map(de=>de.name))),G=null;p&&!R.has(p)&&!await ke(p).catch(()=>null)?G=p:(G=`${t.trim()} lead`,R.has(pl(G))&&(G=`${t.trim().slice(0,50).trim()} ${e} lead`));let X=await gr(G,x,{agentCmd:g,agent:n});ti({cwd:X.cwd,sessionName:X.name,agent:n,agentCmd:g});let K=Date.now(),he=Sw(X,"lead","live"),ce=(Array.isArray(f)?f:[]).filter(U=>U&&typeof U=="object").map(U=>({...U})),re=ce[ce.length-1];re&&re.endedAt==null&&(re.endedAt=K,re.endReason==null&&(re.endReason=o==="recover"?"node-offline":"node-transfer")),ce.push({agent:n,sessionId:X.sessionId??null,pinned:X.pinned===!0,startedAt:K,endedAt:null}),he.epochs=ce;let Ae={id:e,name:t.trim(),cwd:X.cwd,workspace:I,leadBox:X.name,boxes:[he],events:[],createdAt:Number.isFinite(d)?d:K,...z?{pendingHandoffHydrate:{artifact:Po,at:K}}:{}};return _e(Ae,"transferred-in",{box:X.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(w)?w:null}:{},branch:I.branch??null,wip:L,...E?{wipAdvanced:E}:{},epoch:ce.length-1,name:Ae.name,cwd:Ae.cwd,workspace:{base:I.base??null,branch:I.branch??null,kind:I.kind,...I.baseRef?{baseRef:I.baseRef}:{},...I.repo?{repo:I.repo}:{},...I.repos?{repos:I.repos.map(({name:U,base:de,baseRef:ae,repo:Y})=>({name:U,base:de,baseRef:ae,repo:Y}))}:{}}}),b.tracks.push(Ae),await se(),{...await Ct(Ae),adopt:{wipApplied:L,wipSkipped:A,...E?{wipAdvanced:E}:{},codeless:P,artifactSource:z}}}catch(j){for(let L of B)await $t(L.base,L.dir).catch(()=>{});throw await Q.rm(x,{recursive:!0,force:!0}).catch(()=>{}),j}}async function tE(e,t,n){let r=await ne(),o=ye(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await se())}async function ta(e,t){e.step=t,await lc(e)}function nE(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 rE(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function PF(e,t,n){if(!rE(n)||Mt.has(t)||!await ke(n.box).catch(()=>null))return!1;try{await Q.access(n.dir)}catch{return!1}let o=await ke(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Mt.set(t,s),sE(s,o).catch(a=>oE(s,a)),!0}async function Rw(){let e=await ne(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Mt.has(r))continue;if(await PF(n,r,o)){t=!0,_e(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ue({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=nE(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Wr.has(i.state)&&(i.state="canceled"),_e(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 ue({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await NA(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Wr.has(r.state)||Mt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?te.join(ia,`${n.id}-${r.subtaskId}`):null;await ue({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await NA(n,r.name,o)}}return t=await DF(e)||t,t&&await se(),t}async function NA(e,t,n){if(t&&await Qt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Pt(e.workspace)){let s=te.join(n,o.name);await $t(o.base,s).then(()=>!0,()=>!1)||await $t(o.dir,s).catch(()=>{})}await Q.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ke(e.leadBox).catch(()=>null);r?.cwd&&await $t(r.cwd,n).catch(()=>{})}async function DF(e){let t;try{t=await Q.readdir(ia)}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 Mt.values())o.dir&&n.add(te.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=te.join(ia,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Pt(a.workspace))await $t(i.base,te.join(s,i.name)).catch(()=>{});await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ke(a.leadBox).catch(()=>null):null;i?.cwd?await $t(i.cwd,s).catch(()=>{}):await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ue({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Cw(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Rs(n))throw new Error("task (string) required");let s=await rc(t,r),a=ko(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=ye(await ne(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ke(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=Pt(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let h of p)if(!h?.dir||!await Os(h.dir))throw new Error(`tagged checkout "${h?.name??"?"}" is missing from the lead workspace (${h?.dir??"no dir"})`);u=p.map(h=>({name:h.name,leadDir:h.dir,base:null}))}else if(!await Os(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=jo().slice(0,8),d={id:f,trackId:e,trackName:i.name,agent:t,task:Rs(n),skipPermissions:r,reason:LA(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Ow(i.name)}-${f}`,base:null,repos:u,dir:te.join(ia,`${e}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Mt.set(f,d),await lc(d),BF(d,c,s).catch(p=>oE(d,p)),d}async function oE(e,t){e.failedStep=e.step,e.error=t instanceof tc?"canceled":t?.message||String(t),e.step=t instanceof tc?"canceled":"error",e.finishedAt=Date.now(),await Nw(e).catch(()=>{}),await lc(e).catch(()=>{}),await UF(e).catch(()=>{})}function sd(e){let t=Mt.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 Mw(e){let t=Mt.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 BF(e,t,n){if(await ta(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Cs(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await lc(e)}else e.base=await Cs(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await ta(e,"spawn"),await Q.mkdir(ia,{recursive:!0}),e.repos){await Q.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Ms(a.leadDir,te.join(e.dir,a.name),e.branch,a.base);let s=Pt((ye(await ne(),e.trackId)||{}).workspace);await ic(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 Ms(t.cwd,e.dir,e.branch,e.base),await Zi(e.dir);await jF(e,t);let r=e.trackName.slice(0,38).trim(),o=await gr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await LF(e,o),await FA(e.trackId,o.name,jL(e.task,!!e.repos),{kind:"subtask"}),await sE(e,t)}async function sE(e,t){await ta(e,"working");let n=te.join(e.dir,qu);if(await WF(n,e),e.canceled)throw new tc;if(await ta(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=te.join(e.dir,l.name);await Jp(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:CA({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Qp(l.leadDir,l.base,e.branch,ea),l.files=await em(l.leadDir,l.base,e.branch,ea);let f=await tm(l.leadDir,l.base,e.branch,ea);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=>te.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:te.join(l.name,d))),f.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Jp(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:CA({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Qp(t.cwd,e.base,e.branch,ea),e.files=await em(t.cwd,e.base,e.branch,ea);let s=await tm(t.cwd,e.base,e.branch,ea);e.applied=s.applied,e.conflicts=s.conflicts}await lc(e);let r=await HF(e,t,n);await ta(e,"resume"),await FA(e.trackId,e.leadBox,LL(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await ta(e,"reap"),await Nw(e),e.step="done";let o=Date.now();await tE(e.trackId,e.id,{...od(e),finishedAt:o}),await FF(e,o),e.finishedAt=o}async function jF(e,t){let n=te.join(e.dir,te.dirname(zi));await Q.mkdir(n,{recursive:!0});let r="";try{r=await Yb(t.cwd,t.sessionId)}catch{}if(!r)try{r=await Q.readFile(te.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}
|
|
245
245
|
|
|
246
246
|
**Delegated task:** ${e.task}
|
|
247
247
|
|
|
@@ -252,15 +252,15 @@ ${a}`}async function zh(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
252
252
|
## Conversation so far (lead \u2192 you)
|
|
253
253
|
|
|
254
254
|
${r}
|
|
255
|
-
`;await
|
|
255
|
+
`;await Q.writeFile(te.join(e.dir,zi),o)}async function LF(e,t){let n=await ne(),r=ye(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Ea(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),_e(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]||{},...od(e)},await se())}async function FF(e,t=e.finishedAt){let n=await ne(),r=ye(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),_e(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 se()}async function UF(e){let t=await ne(),n=ye(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"),_e(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]||{},...od(e)},await se()}async function HF(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=te.join(t.cwd,r);await Q.mkdir(te.dirname(o),{recursive:!0}),await Q.copyFile(n,o)}catch{}return r}async function Nw(e){if(e.dir&&await bf(e.dir).catch(()=>{}),e.box&&await Qt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await $t(t.leadDir,te.join(e.dir,t.name)).catch(()=>{});await Q.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await ke(e.leadBox).catch(()=>null))?.cwd;n&&await $t(n,e.dir).catch(()=>{})}}async function WF(e,t){let n=(t.startedAt||Date.now())+CF;for(;;){if(t.canceled||t.forceMerge)return;try{await Q.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Bo(MF)}}function Bo(e){return new Promise(t=>setTimeout(t,e))}var PA,$n,DA,PL,Vi,DL,Ku,nc,Hr,Po,BA,BL,qu,zi,jL,LL,Kt,Bh,ye,jh,_e,Lh,Fh,HL,Wr,$A,GL,KL,VL,zL,XL,Yi,RA,eF,tF,nF,rF,oF,na,Ji,cF,uF,dF,mF,zA,Xi,YA,yF,Ki,Uh,bF,Hh,SF,Qi,TF,AF,OA,Wh,Yh,Sw,aa,la,Jh,an,Xh,Aw,qh,CF,MF,ia,ea,Mt,NF,Uo,od,lc,Ow,tc,Yn=H(()=>{Pa();ut();Sp();E0();ni();fr();kl();O0();ct();hn();es();bl();z0();lo();Gp();Vp();dx();El();ip();xl();wp();tn();Hl();ur();bn();ri();Vl();jx();cm();YS();yp();Cl();XS();eh();ge();bu();Mu();_i();xo();Du();Xs();Th();Hu();Oh();Wn();Au();vA();Ph();IA();al();PA=NL(ML),$n=process.env.BOSTRAT_STATE_DIR||te.join(process.env.HOME||"/tmp",".bostrat"),DA=te.join($n,"tracks.json"),PL=te.join($n,"events.jsonl"),Vi=te.join($n,"workspaces"),DL=process.env.BOSTRAT_DEFAULT_REPO||null,Ku={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"}},nc=Object.keys(Ku);Hr={at:0,value:null};Po=".bostrat/handoff/latest.md",BA=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;BL=/\(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\.\)$/;qu=".bostrat/handoff/subtask-result.md",zi=".bostrat/handoff/lead-context.md",jL=(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 ${zi} \u2014 read that first for context. Your scoped task:
|
|
256
256
|
|
|
257
257
|
${e}
|
|
258
258
|
|
|
259
|
-
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 ${
|
|
259
|
+
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 ${qu} (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 ${zi} \u2014 read that first for context. Your scoped task:
|
|
260
260
|
|
|
261
261
|
${e}
|
|
262
262
|
|
|
263
|
-
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 ${
|
|
263
|
+
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 ${qu} 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.`,LL=(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."),Kt=null,Bh=null;ye=(e,t)=>e.tracks.find(n=>n.id===t)||null,jh=Promise.resolve();_e=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),FL(e.id,r),Ux(e,r),r};Qb(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await ne()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(_e(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await se().catch(()=>{}))});Lh=te.join($n,"plane-link","update-notice.json");Fh=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),HL=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Wr=new Set(["merged","conflict","canceled","failed","handed-off"]),$A=new Map;GL=5e3;KL={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};VL=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];zL=10*6e4;XL=1500,Yi=new Map;RA="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.";eF=4e3,tF=3,nF=[300,350,400],rF=1e3,oF=12,na=new Map,Ji=new Map;cF=[250,400,500,600];uF=3e4,dF=[350,650,900];mF=12e4;zA=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}))});Xi="# bostrat workspace",YA=`${Xi}
|
|
264
264
|
|
|
265
265
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
266
266
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -434,6 +434,20 @@ captured automatically. Use this for the decisions made in prose, which would
|
|
|
434
434
|
otherwise evaporate. Do not record trivia, and never let recording block the
|
|
435
435
|
work: it is a ledger, not a gate.
|
|
436
436
|
|
|
437
|
+
COMMIT SUBJECTS carry your intent \u2014 Bostrat reads them to tell fixes from
|
|
438
|
+
planned work (rework attribution, Usage \u2192 Rework). When a commit FIXES
|
|
439
|
+
something (a bug, broken behavior, a wrong earlier approach), start the
|
|
440
|
+
subject \`fix: \u2026\` and add a trailer naming why the fix was needed:
|
|
441
|
+
|
|
442
|
+
Bostrat-Fix-Cause: defect|design|requirements|style
|
|
443
|
+
|
|
444
|
+
(\`defect\` = the earlier code was wrong; \`design\` = the earlier approach had
|
|
445
|
+
to be redone; \`requirements\` = the ask changed; \`style\` = cosmetic.) Planned
|
|
446
|
+
feature/docs/test work uses \`feat:\` / \`docs:\` / \`test:\` subjects and NEVER
|
|
447
|
+
carries Fix-Cause. An unlabeled fix falls back to a card the operator must
|
|
448
|
+
answer by hand \u2014 label at the source instead. This is your own statement about
|
|
449
|
+
your own commit; do not add trailers to commits you did not make.
|
|
450
|
+
|
|
437
451
|
When you FINISH a piece of work and steps remain that ONLY the operator can
|
|
438
452
|
perform (vendor/app registrations, secrets to set, interactive logins, device or
|
|
439
453
|
cert installs, DNS changes, subscriptions, physical steps), write those steps as
|
|
@@ -581,16 +595,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
581
595
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
582
596
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
583
597
|
follow-up on its own; the operator's tap is the gate.
|
|
584
|
-
`,
|
|
585
|
-
`)),
|
|
586
|
-
`,`${
|
|
598
|
+
`,yF=YA,Ki=YA+Lk;Uh=e=>typeof e=="string"&&(e===Xi||e.startsWith(`${Xi}
|
|
599
|
+
`)),bF="# bostrat multi-repo workspace";Hh=Ki.replace(`${Xi}
|
|
600
|
+
`,`${Xi}
|
|
587
601
|
|
|
588
602
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
589
603
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
590
604
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
591
605
|
When working anywhere else, ignore everything below.
|
|
592
|
-
`);fL=new Set(["claude","codex","cursor"]);zi=new Set,pL=e=>zi.has(e);mL=9e4;VT={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};bh=new Map;Eh=120;Qh=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:va(e.hookToken),createdAt:Date.now()});ea=new Map,ra=e=>ea.get(e)||(qt?he(qt,e)?.handoff:null)||null;Ah=6e4,nn=(e,t,n=15e3)=>XT("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),_h=new Map,tw=e=>_h.get(e)||null;kh=new Map;xL=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,vL=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ta=J.join(En,"worktrees"),zs=[Wi,Ru],Nt=new Map,SL=e=>Nt.get(e)||(qt?qt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,jo=e=>{let t=new Map([...Nt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=qt?he(qt,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)},qu=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});oc=e=>gE(e.trackId,e.id,qu(e));iw=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Ji=class extends Error{}});lt();import xe from"node:path";import mt from"node:fs";import XH from"node:readline";import{fileURLToPath as ZH}from"node:url";import Gt from"express";import QH from"node:http";import eW from"node:https";import _d from"node:os";import{WebSocketServer as tW}from"ws";lt();import wO from"node-pty";async function Xy(e,t,n={}){if(!Pe(t)){ul(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await fr()){ul(e,`host tmux server not running on ${$t}`),e.close(1011,"tmux server unavailable");return}if(!await ge(t)){ul(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Jy(n.cols,120,Zy),o=Jy(n.rows,32,Qy),s;try{s=wO.spawn(K,["-S",$t,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ul(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&mf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(mf(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"&&kO(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{}bO(t)})}var gO=6,yO=250;async function bO(e,t=gO,n=yO){for(let r=0;r<t;r++){if(await oo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Zy=20,Qy=6;function kO(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Zy&&Number(t)>=Qy}function Jy(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function mf(e,t){try{e.send(JSON.stringify(t))}catch{}}function ul(e,t){mf(e,{type:"error",message:t})}function eb(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}}Xt();fl();op();Nr();import{createRequire as nW}from"node:module";var ML=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function dw({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(ML)&&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 NE from"node:fs";import Hr from"node:fs/promises";import JL from"node:os";import st from"node:path";import{spawn as XL}from"node:child_process";import{createRequire as ZL}from"node:module";me();Fl();import OE from"node:fs";import ac from"node:fs/promises";import DL from"node:http";import on from"node:path";import Vu from"node:os";import{createRequire as BL}from"node:module";import pw from"ws";import NL from"node:path";import PL from"node:os";var sc=process.env.BOSTRAT_STATE_DIR||NL.join(process.env.HOME||PL.tmpdir(),".bostrat");function fw(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 SE(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=fw(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function TE(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 rn=(()=>{try{return BL(import.meta.url)("../package.json").version||null}catch{return null}})(),EE=process.env.BOSTRAT_STATE_DIR||on.join(process.env.HOME||Vu.tmpdir(),".bostrat");function Lo({env:e=process.env,pkgDir:t=on.resolve(on.dirname(new URL(import.meta.url).pathname),".."),exists:n=OE.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(on.join(t,"..",".git"))?"git-checkout":"dev"}function ic(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 Gu(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 jL({home:e=process.env.HOME||Vu.homedir(),env:t=process.env}={}){let n=r=>{try{return OE.statSync(r),!0}catch{return!1}};return{claude:n(on.join(e,".claude",".credentials.json")),codex:n(on.join(t.CODEX_HOME||on.join(e,".codex"),"auth.json"))}}var ot=1,AE=1e3,_E=3e4,LL=6e4,FL=1500,UL=500,Ur=100,HL=2e3,WL=25e3,qL=6e4,CE=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},KL=CE(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),GL=3e4,IE=10*6e4,VL=400,$E=250,zL=CE(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),mw=4,YL=6e4,RE=6e4;function ME({url:e,token:t,nodeId:n=Vu.hostname(),name:r=Vu.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=YL,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:f=null,updatePolicy:d=null,installKind:p=Lo(),selfUrl:h=`http://127.0.0.1:${process.env.PORT||3362}`,onDegraded:w=null,onRecovered:g=null,ingestMode:k=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:S=KL,unackedRetryMs:E=qL,pingMs:R=WL,maxInflightEvents:B=VL,httpTimeoutMs:T=zL,stallBackoffMinMs:_=GL,fetchImpl:O=globalThis.fetch,log:I=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=TE(e);let U=h.replace(/^http/,"ws"),H=new Set,ae=new Map,X=new Map,C=on.join(EE,"plane-link","cursor.json"),Q=on.join(EE,"plane-link","dead-letter.ndjson"),ve=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),W={},be={},se=new Map,pe=null,ie=null,L=0,ce=0,ue=0,Ae=Promise.resolve(),_e="ws",xt=0,je=!1,A=0,G=!1,Ge=Ur,_t=new Set,at=[],nt=null,y=null,m=!1,b=!1,M=AE,ne=null,de=null,De=null,ht=null,Re=null,ya=!1,Mc=0;async function VI(){try{W=JSON.parse(await ac.readFile(C,"utf8"))}catch{W={}}}function zI(){ya=!0,!De&&(De=setTimeout(async()=>{if(De=null,!!ya){ya=!1;try{await ac.mkdir(on.dirname(C),{recursive:!0}),await ac.writeFile(C,JSON.stringify(W))}catch(P){I.error("[plane-link] cursor flush failed:",P?.message||P)}}},HL),De.unref?.())}function rt(P){return!y||y.readyState!==pw.OPEN?!1:(y.send(JSON.stringify(P)),!0)}function ba(P){let D=Date.now();for(let{trackId:x,event:N}of P){let q=se.get(N.id);se.set(N.id,{trackId:x,seq:N.seq,event:N,sentAt:D,firstSentAt:q?.firstSentAt??D,retryAt:D+E,tries:(q?.tries??0)+1,nacks:q?.nacks??0})}return _e==="http"?(ZI(P),!0):rt({v:ot,ch:"events",type:"append",events:P})}function YI(P){let D=null;for(let x of se.values())x.trackId===P&&(D==null||x.seq<D)&&(D=x.seq);return D}function Od(P){let D=be[P];if(D==null)return;let x=YI(P),N=x==null?D:Math.min(D,x-1);N>(W[P]??0)&&(W[P]=N,zI())}function ka(){if(pe=Date.now(),ie!=null){let P=Date.now()-ie;I.log(`[plane-link] spine sync recovered after ${Math.round(P/1e3)}s degraded (${L} stall redial(s))`);let D=ie;ie=null,L=0,ce=0,je=!1,A=0;try{g?.({since:D,downMs:P})}catch(x){I.error(`[plane-link] onRecovered failed: ${x?.message||x}`)}}}function Cd(P){if(!Array.isArray(P)||!P.length)return;let D=new Set;for(let x of P){let N=se.get(x);N&&(se.delete(x),(be[N.trackId]??0)<N.seq&&(be[N.trackId]=N.seq),D.add(N.trackId))}for(let x of D)Od(x);ka()}function Eg(P,D){ue++;let x={at:Date.now(),trackId:P.trackId,seq:P.seq,id:P.event?.id??null,type:P.event?.type??null,reason:D,event:P.event??null};I.error(`[plane-link] DEAD-LETTER ${P.trackId} seq ${P.seq} (${x.type??"?"}): plane rejected it permanently (${D}) \u2014 kept in ${Q}`),Ae=Ae.then(async()=>{await ac.mkdir(on.dirname(Q),{recursive:!0}),await ac.appendFile(Q,JSON.stringify(x)+`
|
|
593
|
-
`)}).catch(N=>I.error(`[plane-link] dead-letter write failed: ${N?.message||N}`))}function Ag(P,D,x,N=null){if(!Array.isArray(P)||!P.length)return;let q=new Set,ye=Date.now();for(let Ee of P){let Be=se.get(Ee);Be&&(x?(se.delete(Ee),Eg(Be,D),(be[Be.trackId]??0)<Be.seq&&(be[Be.trackId]=Be.seq),q.add(Be.trackId)):(Be.nacks++,Be.retryAt=ye+Math.max(1e3,Number(N)||E)))}for(let Ee of q)Od(Ee);ka()}function _g(P=Date.now()){let D=1/0;for(let x of se.values())x.firstSentAt<D&&(D=x.firstSentAt);return D===1/0?0:P-D}function Ig(){if(!m||!se.size)return;let P=Date.now(),D=_g(P);if(D<S||pe!=null&&P-pe<S||_e==="http"&&je||ce>P)return;L++;let x=Math.min(_*2**(L-1),IE);if(ie==null){ie=P-D,I.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(D/1e3)}s with ${se.size} event(s) in flight (${_e} door) \u2014 the plane is not answering appends`);try{w?.({since:ie,unacked:se.size,oldestAgeMs:D,mode:_e})}catch(N){I.error(`[plane-link] onDegraded failed: ${N?.message||N}`)}}if(_e==="http"){je=!0,A=P+x,I.error(`[plane-link] ingest circuit OPEN (stall #${L}); probing /v1/ingest/health in ${Math.round(x/1e3)}s`);return}ce=P+x,I.error(`[plane-link] closing the link and redialing in ${Math.round(x/1e3)}s (stall redial #${L})`);try{y?.terminate()}catch{}}function Md(){if(!m)return;let P=Date.now();if(_e==="http"&&je)return;let D=[...se.values()].filter(N=>N.event&&N.retryAt<=P&&!(_e==="http"&&_t.has(N.trackId)));if(!D.length)return;D.sort((N,q)=>N.trackId===q.trackId?N.seq-q.seq:N.trackId<q.trackId?-1:1),I.log(`[plane-link] re-sending ${D.length} unacked event(s) the plane never confirmed`);let x=_e==="http"?Ge:Ur;if(_e==="http"){let N=new Map;for(let q of D)(N.get(q.trackId)??N.set(q.trackId,[]).get(q.trackId)).push(q);for(let[,q]of N){if(xt>=mw)return;ba(q.slice(0,x).map(ye=>({trackId:ye.trackId,event:ye.event})))}return}for(let N=0;N<D.length;N+=x){let q=D.slice(N,N+x).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!ba(q))return}}async function JI(P=null){for(;;){if(!m)return!1;let D=se.size<B,x=_e!=="http"||xt<mw&&!je&&(!P||!_t.has(P));if(D&&x)return!0;await new Promise(N=>setTimeout(N,100))}}function Nd(){if(nt=null,!m||!at.length)return;let P=at;if(at=[],_e!=="http"){for(let x=0;x<P.length;x+=Ur)ba(P.slice(x,x+Ur));return}let D=new Map;for(let x of P)(D.get(x.trackId)??D.set(x.trackId,[]).get(x.trackId)).push(x);for(let[x,N]of D){if(_t.has(x)||xt>=mw||je){at.push(...N);continue}for(let q=0;q<N.length;q+=Ge)ba(N.slice(q,q+Ge))}at.length&&!nt&&(nt=setTimeout(Nd,$E),nt.unref?.())}let XI=P=>{let D=new Map;for(let{trackId:x,event:N}of P)(D.get(x)??D.set(x,[]).get(x)).push(N);return[...D].map(([x,N])=>({trackId:x,events:N}))},zo=(P,D,x,{progress:N})=>{let q=Date.now();for(let{event:ye}of P){let Ee=se.get(ye.id);Ee&&(Ee.nacks++,Ee.retryAt=q+Math.max(1e3,x||E))}N&&ka()};async function ZI(P){let D=new Set(P.map(x=>x.trackId));for(let x of D)_t.add(x);xt++;try{let x=await O(`${ve}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:XI(P)}),signal:AbortSignal.timeout(T)}),N=(Number(x.headers.get("retry-after"))||0)*1e3;if(x.status===200){let q=await x.json();Ge=Ur;for(let ye of q.results??[]){Cd(ye.acked??[]);let Ee=(Be,vt)=>{let Lt=new Map;for(let Ye of Be??[])(Lt.get(Ye.reason)??Lt.set(Ye.reason,[]).get(Ye.reason)).push(Ye.id);for(let[Ye,Mg]of Lt)Ag(Mg,Ye,vt,q.retryAfterMs??null)};Ee(ye.rejected,!0),Ee(ye.deferred,!1)}Cd(q.discarded??[]),ka();return}if(x.status===429){zo(P,"overloaded",N||5e3,{progress:!0});return}if(x.status===413&&P.length>1){Ge=Math.max(1,Math.floor(P.length/2)),I.error(`[plane-link] ingest request too large (${P.length} events) \u2014 halving batches to ${Ge}`),zo(P,"too_large",1e3,{progress:!0});return}if(x.status===413){let q=se.get(P[0].event.id);q&&(se.delete(q.event.id),Eg(q,"too_large"),(be[q.trackId]??0)<q.seq&&(be[q.trackId]=q.seq),Od(q.trackId)),ka();return}if(x.status===401||x.status===403){if(Date.now()-Mc>=RE){Mc=Date.now();let q="";try{q=(await x.json())?.error??""}catch{}I.error(`[plane-link] plane refused ingest (HTTP ${x.status}${q?`: ${q}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}zo(P,`http_${x.status}`,6e4,{progress:!1});return}if(x.status===400){I.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${P.length} event(s) for retry; this is a bug, not data`),zo(P,"http_400",6e4,{progress:!1});return}zo(P,`http_${x.status}`,N||5e3,{progress:!1})}catch(x){let N=x?.name==="TimeoutError"||x?.name==="AbortError";N&&P.length>1&&(Ge=Math.max(1,Math.floor(P.length/2)),I.error(`[plane-link] ingest request of ${P.length} event(s) outlived its ${T} ms deadline \u2014 halving batches to ${Ge}`)),zo(P,N?"timeout":"network",5e3,{progress:!1})}finally{xt--;for(let x of D)_t.delete(x)}}async function QI(){if(_e!=="http"||!je||!m||G)return;let P=Date.now();if(!(A>P)){G=!0;try{let D=await O(`${ve}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(T)});if(!D.ok)throw new Error(`HTTP ${D.status}`);I.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),je=!1,A=0,ce=Date.now()+T+E+1e3;for(let x of se.values())x.retryAt=Math.min(x.retryAt,Date.now());Md()}catch(D){L++;let x=Math.min(_*2**(L-1),IE);A=Date.now()+x,I.error(`[plane-link] ingest door still not answering (${D?.message||D}); next probe in ${Math.round(x/1e3)}s`)}finally{G=!1}}}function $g(){let P=Date.now();return{mode:_e,unacked:se.size,oldestUnackedAgeMs:_g(P),lastProgressAt:pe,degradedSince:ie,stallRedials:L,deadLettered:ue,http:_e==="http"?{inFlight:xt,circuitOpen:je,nextProbeInMs:je?Math.max(0,A-P):0,batchCap:Ge}:null}}async function Rg(){if(!i)return;let P;try{P=await i()}catch(D){I.error("[plane-link] metrics sample failed:",D?.message||D);return}P&&rt({v:ot,ch:"ctl",type:"metrics",metrics:{...P,planeLink:$g()}})}function e$(){i&&(Re&&clearInterval(Re),Rg(),Re=setInterval(Rg,c),Re.unref?.())}async function t$(){let P;try{P=await o()}catch(D){I.error("[plane-link] backfill listTracks failed:",D?.message||D);return}rt({v:ot,ch:"ctl",type:"tracks",ids:P.filter(D=>!D.transferredTo).map(D=>D.id).filter(D=>typeof D=="string")});for(let D of P){if(!m)return;let x;try{x=await F(D)}catch{continue}let N=W[D.id]??0,q=x.filter(Ee=>Ee.seq>N&&typeof Ee.id=="string"),ye=_e==="http"?Ge:Ur;for(let Ee=0;Ee<q.length;Ee+=ye){if(!await JI(D.id))return;let Be=q.slice(Ee,Ee+Math.min(ye,_e==="http"?Ge:Ur)).map(vt=>({trackId:D.id,event:vt}));if(!ba(Be))return}}}function Og(P){!m||!P?.trackId||typeof P.id!="string"||(at.push({trackId:P.trackId,event:P}),nt||(nt=setTimeout(Nd,$E),nt.unref?.()))}let xa=new Map;async function n$(P){let{reqId:D,method:x,path:N,headers:q={},body:ye}=P,Ee=Object.fromEntries(Object.entries(q).filter(([,vt])=>vt!=null)),Be=new AbortController;xa.set(D,Be);try{if(P.host){await r$(P,Ee,Be);return}let vt=await fetch(h+N,{method:x,headers:Ee,body:ye?Buffer.from(ye,"base64"):void 0,signal:Be.signal}),Lt=Object.fromEntries(vt.headers);for(let Ye of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Lt[Ye];if(Lt["content-type"]||(Lt["content-type"]="application/octet-stream"),rt({v:ot,ch:"control",type:"http_head",reqId:D,status:vt.status,headers:Lt}),vt.body){for await(let Ye of vt.body)if(!rt({v:ot,ch:"control",type:"http_data",reqId:D,data:Buffer.from(Ye).toString("base64")}))break}rt({v:ot,ch:"control",type:"http_end",reqId:D,error:null})}finally{xa.delete(D)}}function r$(P,D,x){let{reqId:N,method:q,path:ye,body:Ee}=P,Be=new URL(h);return new Promise(vt=>{let Lt=DL.request({host:Be.hostname,port:Be.port,method:q,path:ye,headers:{...D,host:P.host},signal:x.signal},Ye=>{let{connection:Mg,"keep-alive":kW,"transfer-encoding":xW,upgrade:vW,...o$}=Ye.headers;rt({v:ot,ch:"control",type:"http_head",reqId:N,status:Ye.statusCode||502,headers:o$}),Ye.on("data",s$=>{rt({v:ot,ch:"control",type:"http_data",reqId:N,data:Buffer.from(s$).toString("base64")})||Ye.destroy()}),Ye.on("end",()=>{rt({v:ot,ch:"control",type:"http_end",reqId:N,error:null}),vt()}),Ye.on("error",()=>{rt({v:ot,ch:"control",type:"http_end",reqId:N,error:"view upstream error"}),vt()})});Lt.on("error",Ye=>{rt({v:ot,ch:"control",type:"http_end",reqId:N,error:Ye?.message||"view request failed"}),vt()}),Ee&&Lt.write(Buffer.from(Ee,"base64")),Lt.end()})}function Cg(){if(b)return;y=new pw(e,{headers:{authorization:`Bearer ${t}`}}),y.on("open",async()=>{let D=null;try{D=await s?.()||null}catch{D=null}rt({v:ot,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:rn,installKind:p,...typeof d=="function"?{updatePolicy:d()}:d?{updatePolicy:d}:{},providers:jL(),...D?{agents:D}:{}})}),y.on("message",D=>{let x;try{x=JSON.parse(D)}catch{return}if(x.ch==="ctl"&&x.type==="hello_ok"){m=!0,M=AE,ne=null,I.log(`[plane-link] connected to plane as ${n}`),x.minNodeVersion&&rn&&ic(rn,x.minNodeVersion)&&I.error(`[plane-link] this node is bostrat-node ${rn}; the plane asks for \u2265${x.minNodeVersion} \u2014 update with: ${Gu(p)}`);try{u?.(typeof x.updatePolicy=="string"?x.updatePolicy:null)}catch{}let N=typeof x.latestNodeVersion=="string"?x.latestNodeVersion:null;if(N&&rn&&ic(rn,N)&&!H.has(N)){H.add(N),I.log(`[plane-link] bostrat-node ${N} is available (this node runs ${rn}) \u2014 ${Gu(p)}`);try{l?.({current:rn,latest:N,min:x.minNodeVersion??null,installKind:p,required:!!(x.minNodeVersion&&ic(rn,x.minNodeVersion))})}catch(ye){I.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}let q=x.ingest==="http";_e=k==="ws"?"ws":k==="http"||q?"http":"ws",k==="http"&&!q&&I.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),_e==="http"&&I.log("[plane-link] spine rides the HTTPS ingest door"),je=!1,A=0,Ge=Ur,ht&&clearInterval(ht),ht=setInterval(()=>{rt({v:ot,ch:"ctl",type:"ping"}),Ig(),QI().catch(()=>{}),Md(),at.length&&!nt&&Nd()},R),ht.unref?.(),e$(),t$().catch(ye=>I.error("[plane-link] backfill failed:",ye?.message||ye));return}if(x.ch==="ctl"&&x.type==="hello_refused"){let N=typeof x.minNodeVersion=="string"?x.minNodeVersion:null,q=typeof x.latestNodeVersion=="string"?x.latestNodeVersion:null;I.error(`[plane-link] plane REFUSED this node: bostrat-node ${rn??"?"} is below its minimum ${N??"?"} \u2014 ${x.fix||Gu(p)}`),M=_E,ne=0;try{f?.({current:rn,min:N,latest:q,fix:x.fix||Gu(p),installKind:p})}catch(ye){I.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}return}if(x.ch==="ctl"&&x.type==="update_policy"){try{u?.(typeof x.mode=="string"?x.mode:null)}catch{}return}if(x.ch==="ctl"&&x.type==="usage_caps"){Ll(x.caps??{},{source:"plane",updatedAt:x.updatedAt??Date.now()}).then(N=>I.log(`[plane-link] spend caps ${Object.keys(N.caps).length?`updated (${Object.keys(N.caps).join(", ")})`:"cleared"} from plane`)).catch(N=>I.error("[plane-link] spend-caps push rejected:",N?.message||N));return}if(x.ch==="ctl"&&x.type==="transferred"&&Array.isArray(x.tracks)){Promise.resolve().then(()=>a?.(x.tracks)).catch(N=>I.error("[plane-link] transfer notice failed:",N?.message||N));return}if(x.ch==="events"&&x.type==="ack"&&Array.isArray(x.ids)){Cd(x.ids);return}if(x.ch==="events"&&x.type==="nack"&&Array.isArray(x.ids)){Ag(x.ids,x.reason,x.permanent===!0,x.retryAfterMs??null);return}if(x.ch==="control"&&x.type==="http_open"){n$(x).catch(N=>rt({v:ot,ch:"control",type:"http_end",reqId:x.reqId,error:N?.message||"tunnel error"}));return}if(x.ch==="control"&&x.type==="http_cancel"){xa.get(x.reqId)?.abort();return}if(x.ch==="attach"&&x.type==="open"){let N=new pw(U+x.path,Array.isArray(x.protocols)?x.protocols:[],x.host?{headers:{host:x.host}}:void 0);ae.set(x.streamId,N),X.set(x.streamId,[]),N.on("open",()=>{let ye=X.get(x.streamId)||[];X.delete(x.streamId);for(let{buf:Ee,bin:Be}of ye)N.send(Ee,{binary:Be})}),N.on("message",(ye,Ee)=>{let Be=Ee?ye:Buffer.from(String(ye));rt({v:ot,ch:"attach",type:"data",streamId:x.streamId,data:Be.toString("base64"),...Ee?{bin:!0}:{}})});let q=()=>{X.delete(x.streamId),ae.delete(x.streamId)&&rt({v:ot,ch:"attach",type:"close",streamId:x.streamId})};N.on("close",q),N.on("error",q);return}if(x.ch==="attach"&&x.type==="data"){let N=ae.get(x.streamId);if(!N)return;let q=Buffer.from(x.data,"base64"),ye=x.bin===!0,Ee=X.get(x.streamId);Ee?Ee.push({buf:q,bin:ye}):N.send(q,{binary:ye});return}if(x.ch==="attach"&&x.type==="close"){let N=ae.get(x.streamId);ae.delete(x.streamId),X.delete(x.streamId),N?.close();return}});let P=()=>{m&&I.log("[plane-link] disconnected from plane"),m=!1,ht&&(clearInterval(ht),ht=null),Re&&(clearInterval(Re),Re=null),se.clear(),be={},at=[],nt&&(clearTimeout(nt),nt=null),_t.clear();for(let N of ae.values())N.close();ae.clear(),X.clear();for(let N of xa.values())N.abort();if(xa.clear(),b||de)return;ne==null&&(ne=Date.now());let D=Date.now()-ne<LL,x=Math.max(D?FL+Math.random()*UL:M,ce-Date.now());de=setTimeout(()=>{de=null,Cg()},x),de.unref?.(),D||(M=Math.min(M*2,_E))};y.on("close",P),y.on("error",D=>{let x=/Unexpected server response: (\d+)/.exec(D?.message||"")?.[1];(x==="401"||x==="403")&&Date.now()-Mc>=RE&&(Mc=Date.now(),I.error(`[plane-link] plane refused this node's credentials (HTTP ${x}) \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)`)),P()})}return ct.on("event",Og),VI().then(Cg),{stop:()=>{b=!0,ct.off("event",Og),de&&clearTimeout(de),De&&clearTimeout(De),ht&&clearInterval(ht),Re&&clearInterval(Re),nt&&clearTimeout(nt),rt({v:ot,ch:"ctl",type:"bye",reason:"shutdown"}),y?.close()},status:()=>({connected:m,nodeId:n,cursor:{...W},inflight:se.size,...$g()}),_tick:()=>{Ig(),Md()}}}pl();var zu=(()=>{try{return ZL(import.meta.url)("../package.json").version||null}catch{return null}})(),QL=process.env.BOSTRAT_STATE_DIR||st.join(process.env.HOME||JL.tmpdir(),".bostrat"),PE=st.join(QL,"self-update"),Yu=st.join(PE,"status.json"),eF=1440*6e4,tF=6e4,cc=new Set(["npm-global","git-checkout"]);function DE({kind:e=Lo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:cc.has(e)?e==="git-checkout"||nF(t)?"auto":"prompt":"off"}function nF(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Bt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function rF({kind:e,to:t,from:n=zu,statusFile:r=Yu,logFile:o=st.join(PE,"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||st.resolve(st.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!cc.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,k="")=>`write_status ${Bt(g)} ${k}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Bt(i)}`,p=`
|
|
606
|
+
`);SF=new Set(["claude","codex","cursor"]);Qi=new Set,TF=e=>Qi.has(e);AF=9e4;OA={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Wh=new Map;Yh=120;Sw=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ea(e.hookToken),createdAt:Date.now()});aa=new Map,la=e=>aa.get(e)||(Kt?ye(Kt,e)?.handoff:null)||null;Jh=6e4,an=(e,t,n=15e3)=>PA("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Xh=new Map,Aw=e=>Xh.get(e)||null;qh=new Map;CF=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,MF=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ia=te.join($n,"worktrees"),ea=[zi,qu],Mt=new Map,NF=e=>Mt.get(e)||(Kt?Kt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Uo=e=>{let t=new Map([...Mt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Kt?ye(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)},od=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});lc=e=>tE(e.trackId,e.id,od(e));Ow=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",tc=class extends Error{}});ut();import Te from"node:path";import ht from"node:fs";import lq from"node:readline";import{fileURLToPath as uq}from"node:url";import jt from"express";import dq from"node:http";import fq from"node:https";import Kd from"node:os";import{WebSocketServer as pq}from"ws";ut();import cC from"node-pty";async function _b(e,t,n={}){if(!Le(t)){gl(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await wr()){gl(e,`host tmux server not running on ${It}`),e.close(1011,"tmux server unavailable");return}if(!await ke(t)){gl(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Eb(n.cols,120,Ib),o=Eb(n.rows,32,$b),s;try{s=cC.spawn(J,["-S",It,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){gl(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Nf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Nf(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"&&fC(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{}dC(t)})}var lC=6,uC=250;async function dC(e,t=lC,n=uC){for(let r=0;r<t;r++){if(await ao(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Ib=20,$b=6;function fC(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Ib&&Number(t)>=$b}function Eb(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Nf(e,t){try{e.send(JSON.stringify(t))}catch{}}function gl(e,t){Nf(e,{type:"error",message:t})}function Rb(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}}tn();bl();Sp();Yn();import{createRequire as mq}from"node:module";var qF=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Pw({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(qF)&&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 yE from"node:fs";import Gr from"node:fs/promises";import iU from"node:os";import it from"node:path";import{spawn as cU}from"node:child_process";import{createRequire as lU}from"node:module";ge();Vl();import hE from"node:fs";import dc from"node:fs/promises";import VF from"node:http";import ln from"node:path";import id from"node:os";import{createRequire as zF}from"node:module";import Bw from"ws";import GF from"node:path";import KF from"node:os";var uc=process.env.BOSTRAT_STATE_DIR||GF.join(process.env.HOME||KF.tmpdir(),".bostrat");function Dw(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 iE(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Dw(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function cE(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 cn=(()=>{try{return zF(import.meta.url)("../package.json").version||null}catch{return null}})(),lE=process.env.BOSTRAT_STATE_DIR||ln.join(process.env.HOME||id.tmpdir(),".bostrat");function Ho({env:e=process.env,pkgDir:t=ln.resolve(ln.dirname(new URL(import.meta.url).pathname),".."),exists:n=hE.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(ln.join(t,"..",".git"))?"git-checkout":"dev"}function fc(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 ad(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 YF({home:e=process.env.HOME||id.homedir(),env:t=process.env}={}){let n=r=>{try{return hE.statSync(r),!0}catch{return!1}};return{claude:n(ln.join(e,".claude",".credentials.json")),codex:n(ln.join(t.CODEX_HOME||ln.join(e,".codex"),"auth.json"))}}var at=1,uE=1e3,dE=3e4,JF=6e4,XF=1500,ZF=500,qr=100,QF=2e3,eU=25e3,tU=6e4,wE=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},nU=wE(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),rU=3e4,fE=10*6e4,oU=400,pE=250,sU=wE(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),jw=4,aU=6e4,mE=6e4;function gE({url:e,token:t,nodeId:n=id.hostname(),name:r=id.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=aU,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:f=null,updatePolicy:d=null,installKind:p=Ho(),selfUrl:h=`http://127.0.0.1:${process.env.PORT||3362}`,onDegraded:w=null,onRecovered:g=null,onConnected:b=null,ingestMode:v=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:x=nU,unackedRetryMs:I=tU,pingMs:P=eU,maxInflightEvents:B=oU,httpTimeoutMs:j=sU,stallBackoffMinMs:L=rU,fetchImpl:A=globalThis.fetch,log:E=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=cE(e);let q=h.replace(/^http/,"ws"),z=new Set,F=new Map,R=new Map,G=ln.join(lE,"plane-link","cursor.json"),X=ln.join(lE,"plane-link","dead-letter.ndjson"),K=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),he={},ce={},re=new Map,Ae=null,U=null,de=0,ae=0,Y=0,Re=Promise.resolve(),pe="ws",Ie=0,_=!1,N=0,xe=!1,Ne=qr,Xe=new Set,wt=[],y=null,m=null,k=!1,O=!1,le=uE,we=null,Fe=null,Lt=null,Me=null,Jt=null,Jd=!1,Fc=0;async function L$(){try{he=JSON.parse(await dc.readFile(G,"utf8"))}catch{he={}}}function F$(){Jd=!0,!Lt&&(Lt=setTimeout(async()=>{if(Lt=null,!!Jd){Jd=!1;try{await dc.mkdir(ln.dirname(G),{recursive:!0}),await dc.writeFile(G,JSON.stringify(he))}catch(M){E.error("[plane-link] cursor flush failed:",M?.message||M)}}},QF),Lt.unref?.())}function st(M){return!m||m.readyState!==Bw.OPEN?!1:(m.send(JSON.stringify(M)),!0)}function Sa(M){let D=Date.now();for(let{trackId:T,event:C}of M){let V=re.get(C.id);re.set(C.id,{trackId:T,seq:C.seq,event:C,sentAt:D,firstSentAt:V?.firstSentAt??D,retryAt:D+I,tries:(V?.tries??0)+1,nacks:V?.nacks??0})}return pe==="http"?(q$(M),!0):st({v:at,ch:"events",type:"append",events:M})}function U$(M){let D=null;for(let T of re.values())T.trackId===M&&(D==null||T.seq<D)&&(D=T.seq);return D}function Xd(M){let D=ce[M];if(D==null)return;let T=U$(M),C=T==null?D:Math.min(D,T-1);C>(he[M]??0)&&(he[M]=C,F$())}function Ta(){if(Ae=Date.now(),U!=null){let M=Date.now()-U;E.log(`[plane-link] spine sync recovered after ${Math.round(M/1e3)}s degraded (${de} stall redial(s))`);let D=U;U=null,de=0,ae=0,_=!1,N=0;try{g?.({since:D,downMs:M})}catch(T){E.error(`[plane-link] onRecovered failed: ${T?.message||T}`)}}}function Zd(M){if(!Array.isArray(M)||!M.length)return;let D=new Set;for(let T of M){let C=re.get(T);C&&(re.delete(T),(ce[C.trackId]??0)<C.seq&&(ce[C.trackId]=C.seq),D.add(C.trackId))}for(let T of D)Xd(T);Ta()}function ty(M,D){Y++;let T={at:Date.now(),trackId:M.trackId,seq:M.seq,id:M.event?.id??null,type:M.event?.type??null,reason:D,event:M.event??null};E.error(`[plane-link] DEAD-LETTER ${M.trackId} seq ${M.seq} (${T.type??"?"}): plane rejected it permanently (${D}) \u2014 kept in ${X}`),Re=Re.then(async()=>{await dc.mkdir(ln.dirname(X),{recursive:!0}),await dc.appendFile(X,JSON.stringify(T)+`
|
|
607
|
+
`)}).catch(C=>E.error(`[plane-link] dead-letter write failed: ${C?.message||C}`))}function ny(M,D,T,C=null){if(!Array.isArray(M)||!M.length)return;let V=new Set,ve=Date.now();for(let $e of M){let Ue=re.get($e);Ue&&(T?(re.delete($e),ty(Ue,D),(ce[Ue.trackId]??0)<Ue.seq&&(ce[Ue.trackId]=Ue.seq),V.add(Ue.trackId)):(Ue.nacks++,Ue.retryAt=ve+Math.max(1e3,Number(C)||I)))}for(let $e of V)Xd($e);Ta()}function ry(M=Date.now()){let D=1/0;for(let T of re.values())T.firstSentAt<D&&(D=T.firstSentAt);return D===1/0?0:M-D}function oy(){if(!k||!re.size)return;let M=Date.now(),D=ry(M);if(D<x||Ae!=null&&M-Ae<x||pe==="http"&&_||ae>M)return;de++;let T=Math.min(L*2**(de-1),fE);if(U==null){U=M-D,E.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(D/1e3)}s with ${re.size} event(s) in flight (${pe} door) \u2014 the plane is not answering appends`);try{w?.({since:U,unacked:re.size,oldestAgeMs:D,mode:pe})}catch(C){E.error(`[plane-link] onDegraded failed: ${C?.message||C}`)}}if(pe==="http"){_=!0,N=M+T,E.error(`[plane-link] ingest circuit OPEN (stall #${de}); probing /v1/ingest/health in ${Math.round(T/1e3)}s`);return}ae=M+T,E.error(`[plane-link] closing the link and redialing in ${Math.round(T/1e3)}s (stall redial #${de})`);try{m?.terminate()}catch{}}function Qd(){if(!k)return;let M=Date.now();if(pe==="http"&&_)return;let D=[...re.values()].filter(C=>C.event&&C.retryAt<=M&&!(pe==="http"&&Xe.has(C.trackId)));if(!D.length)return;D.sort((C,V)=>C.trackId===V.trackId?C.seq-V.seq:C.trackId<V.trackId?-1:1),E.log(`[plane-link] re-sending ${D.length} unacked event(s) the plane never confirmed`);let T=pe==="http"?Ne:qr;if(pe==="http"){let C=new Map;for(let V of D)(C.get(V.trackId)??C.set(V.trackId,[]).get(V.trackId)).push(V);for(let[,V]of C){if(Ie>=jw)return;Sa(V.slice(0,T).map(ve=>({trackId:ve.trackId,event:ve.event})))}return}for(let C=0;C<D.length;C+=T){let V=D.slice(C,C+T).map(ve=>({trackId:ve.trackId,event:ve.event}));if(!Sa(V))return}}async function H$(M=null){for(;;){if(!k)return!1;let D=re.size<B,T=pe!=="http"||Ie<jw&&!_&&(!M||!Xe.has(M));if(D&&T)return!0;await new Promise(C=>setTimeout(C,100))}}function ef(){if(y=null,!k||!wt.length)return;let M=wt;if(wt=[],pe!=="http"){for(let T=0;T<M.length;T+=qr)Sa(M.slice(T,T+qr));return}let D=new Map;for(let T of M)(D.get(T.trackId)??D.set(T.trackId,[]).get(T.trackId)).push(T);for(let[T,C]of D){if(Xe.has(T)||Ie>=jw||_){wt.push(...C);continue}for(let V=0;V<C.length;V+=Ne)Sa(C.slice(V,V+Ne))}wt.length&&!y&&(y=setTimeout(ef,pE),y.unref?.())}let W$=M=>{let D=new Map;for(let{trackId:T,event:C}of M)(D.get(T)??D.set(T,[]).get(T)).push(C);return[...D].map(([T,C])=>({trackId:T,events:C}))},Qo=(M,D,T,{progress:C})=>{let V=Date.now();for(let{event:ve}of M){let $e=re.get(ve.id);$e&&($e.nacks++,$e.retryAt=V+Math.max(1e3,T||I))}C&&Ta()};async function q$(M){let D=new Set(M.map(T=>T.trackId));for(let T of D)Xe.add(T);Ie++;try{let T=await A(`${K}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:W$(M)}),signal:AbortSignal.timeout(j)}),C=(Number(T.headers.get("retry-after"))||0)*1e3;if(T.status===200){let V=await T.json();Ne=qr;for(let ve of V.results??[]){Zd(ve.acked??[]);let $e=(Ue,vt)=>{let Ft=new Map;for(let Ze of Ue??[])(Ft.get(Ze.reason)??Ft.set(Ze.reason,[]).get(Ze.reason)).push(Ze.id);for(let[Ze,ly]of Ft)ny(ly,Ze,vt,V.retryAfterMs??null)};$e(ve.rejected,!0),$e(ve.deferred,!1)}Zd(V.discarded??[]),Ta();return}if(T.status===429){Qo(M,"overloaded",C||5e3,{progress:!0});return}if(T.status===413&&M.length>1){Ne=Math.max(1,Math.floor(M.length/2)),E.error(`[plane-link] ingest request too large (${M.length} events) \u2014 halving batches to ${Ne}`),Qo(M,"too_large",1e3,{progress:!0});return}if(T.status===413){let V=re.get(M[0].event.id);V&&(re.delete(V.event.id),ty(V,"too_large"),(ce[V.trackId]??0)<V.seq&&(ce[V.trackId]=V.seq),Xd(V.trackId)),Ta();return}if(T.status===401||T.status===403){if(Date.now()-Fc>=mE){Fc=Date.now();let V="";try{V=(await T.json())?.error??""}catch{}E.error(`[plane-link] plane refused ingest (HTTP ${T.status}${V?`: ${V}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Qo(M,`http_${T.status}`,6e4,{progress:!1});return}if(T.status===400){E.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${M.length} event(s) for retry; this is a bug, not data`),Qo(M,"http_400",6e4,{progress:!1});return}Qo(M,`http_${T.status}`,C||5e3,{progress:!1})}catch(T){let C=T?.name==="TimeoutError"||T?.name==="AbortError";C&&M.length>1&&(Ne=Math.max(1,Math.floor(M.length/2)),E.error(`[plane-link] ingest request of ${M.length} event(s) outlived its ${j} ms deadline \u2014 halving batches to ${Ne}`)),Qo(M,C?"timeout":"network",5e3,{progress:!1})}finally{Ie--;for(let T of D)Xe.delete(T)}}async function G$(){if(pe!=="http"||!_||!k||xe)return;let M=Date.now();if(!(N>M)){xe=!0;try{let D=await A(`${K}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(j)});if(!D.ok)throw new Error(`HTTP ${D.status}`);E.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),_=!1,N=0,ae=Date.now()+j+I+1e3;for(let T of re.values())T.retryAt=Math.min(T.retryAt,Date.now());Qd()}catch(D){de++;let T=Math.min(L*2**(de-1),fE);N=Date.now()+T,E.error(`[plane-link] ingest door still not answering (${D?.message||D}); next probe in ${Math.round(T/1e3)}s`)}finally{xe=!1}}}function sy(){let M=Date.now();return{mode:pe,unacked:re.size,oldestUnackedAgeMs:ry(M),lastProgressAt:Ae,degradedSince:U,stallRedials:de,deadLettered:Y,http:pe==="http"?{inFlight:Ie,circuitOpen:_,nextProbeInMs:_?Math.max(0,N-M):0,batchCap:Ne}:null}}async function ay(){if(!i)return;let M;try{M=await i()}catch(D){E.error("[plane-link] metrics sample failed:",D?.message||D);return}M&&st({v:at,ch:"ctl",type:"metrics",metrics:{...M,planeLink:sy()}})}function K$(){i&&(Jt&&clearInterval(Jt),ay(),Jt=setInterval(ay,c),Jt.unref?.())}async function V$(){let M;try{M=await o()}catch(D){E.error("[plane-link] backfill listTracks failed:",D?.message||D);return}st({v:at,ch:"ctl",type:"tracks",ids:M.filter(D=>!D.transferredTo).map(D=>D.id).filter(D=>typeof D=="string")});for(let D of M){if(!k)return;let T;try{T=await W(D)}catch{continue}let C=he[D.id]??0,V=T.filter($e=>$e.seq>C&&typeof $e.id=="string"),ve=pe==="http"?Ne:qr;for(let $e=0;$e<V.length;$e+=ve){if(!await H$(D.id))return;let Ue=V.slice($e,$e+Math.min(ve,pe==="http"?Ne:qr)).map(vt=>({trackId:D.id,event:vt}));if(!Sa(Ue))return}}}function iy(M){!k||!M?.trackId||typeof M.id!="string"||(wt.push({trackId:M.trackId,event:M}),y||(y=setTimeout(ef,pE),y.unref?.()))}let Aa=new Map;async function z$(M){let{reqId:D,method:T,path:C,headers:V={},body:ve}=M,$e=Object.fromEntries(Object.entries(V).filter(([,vt])=>vt!=null)),Ue=new AbortController;Aa.set(D,Ue);try{if(M.host){await Y$(M,$e,Ue);return}let vt=await fetch(h+C,{method:T,headers:$e,body:ve?Buffer.from(ve,"base64"):void 0,signal:Ue.signal}),Ft=Object.fromEntries(vt.headers);for(let Ze of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Ft[Ze];if(Ft["content-type"]||(Ft["content-type"]="application/octet-stream"),st({v:at,ch:"control",type:"http_head",reqId:D,status:vt.status,headers:Ft}),vt.body){for await(let Ze of vt.body)if(!st({v:at,ch:"control",type:"http_data",reqId:D,data:Buffer.from(Ze).toString("base64")}))break}st({v:at,ch:"control",type:"http_end",reqId:D,error:null})}finally{Aa.delete(D)}}function Y$(M,D,T){let{reqId:C,method:V,path:ve,body:$e}=M,Ue=new URL(h);return new Promise(vt=>{let Ft=VF.request({host:Ue.hostname,port:Ue.port,method:V,path:ve,headers:{...D,host:M.host},signal:T.signal},Ze=>{let{connection:ly,"keep-alive":Cq,"transfer-encoding":Mq,upgrade:Nq,...J$}=Ze.headers;st({v:at,ch:"control",type:"http_head",reqId:C,status:Ze.statusCode||502,headers:J$}),Ze.on("data",X$=>{st({v:at,ch:"control",type:"http_data",reqId:C,data:Buffer.from(X$).toString("base64")})||Ze.destroy()}),Ze.on("end",()=>{st({v:at,ch:"control",type:"http_end",reqId:C,error:null}),vt()}),Ze.on("error",()=>{st({v:at,ch:"control",type:"http_end",reqId:C,error:"view upstream error"}),vt()})});Ft.on("error",Ze=>{st({v:at,ch:"control",type:"http_end",reqId:C,error:Ze?.message||"view request failed"}),vt()}),$e&&Ft.write(Buffer.from($e,"base64")),Ft.end()})}function cy(){if(O)return;m=new Bw(e,{headers:{authorization:`Bearer ${t}`}}),m.on("open",async()=>{let D=null;try{D=await s?.()||null}catch{D=null}st({v:at,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:cn,installKind:p,...typeof d=="function"?{updatePolicy:d()}:d?{updatePolicy:d}:{},providers:YF(),...D?{agents:D}:{}})}),m.on("message",D=>{let T;try{T=JSON.parse(D)}catch{return}if(T.ch==="ctl"&&T.type==="hello_ok"){k=!0,le=uE,we=null,E.log(`[plane-link] connected to plane as ${n}`);try{b?.()}catch{}T.minNodeVersion&&cn&&fc(cn,T.minNodeVersion)&&E.error(`[plane-link] this node is bostrat-node ${cn}; the plane asks for \u2265${T.minNodeVersion} \u2014 update with: ${ad(p)}`);try{u?.(typeof T.updatePolicy=="string"?T.updatePolicy:null)}catch{}let C=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;if(C&&cn&&fc(cn,C)&&!z.has(C)){z.add(C),E.log(`[plane-link] bostrat-node ${C} is available (this node runs ${cn}) \u2014 ${ad(p)}`);try{l?.({current:cn,latest:C,min:T.minNodeVersion??null,installKind:p,required:!!(T.minNodeVersion&&fc(cn,T.minNodeVersion))})}catch(ve){E.error(`[plane-link] update notice failed: ${ve?.message||ve}`)}}let V=T.ingest==="http";pe=v==="ws"?"ws":v==="http"||V?"http":"ws",v==="http"&&!V&&E.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),pe==="http"&&E.log("[plane-link] spine rides the HTTPS ingest door"),_=!1,N=0,Ne=qr,Me&&clearInterval(Me),Me=setInterval(()=>{st({v:at,ch:"ctl",type:"ping"}),oy(),G$().catch(()=>{}),Qd(),wt.length&&!y&&ef()},P),Me.unref?.(),K$(),V$().catch(ve=>E.error("[plane-link] backfill failed:",ve?.message||ve));return}if(T.ch==="ctl"&&T.type==="hello_refused"){let C=typeof T.minNodeVersion=="string"?T.minNodeVersion:null,V=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;E.error(`[plane-link] plane REFUSED this node: bostrat-node ${cn??"?"} is below its minimum ${C??"?"} \u2014 ${T.fix||ad(p)}`),le=dE,we=0;try{f?.({current:cn,min:C,latest:V,fix:T.fix||ad(p),installKind:p})}catch(ve){E.error(`[plane-link] refusal handler failed: ${ve?.message||ve}`)}return}if(T.ch==="ctl"&&T.type==="update_policy"){try{u?.(typeof T.mode=="string"?T.mode:null)}catch{}return}if(T.ch==="ctl"&&T.type==="usage_caps"){Kl(T.caps??{},{source:"plane",updatedAt:T.updatedAt??Date.now()}).then(C=>E.log(`[plane-link] spend caps ${Object.keys(C.caps).length?`updated (${Object.keys(C.caps).join(", ")})`:"cleared"} from plane`)).catch(C=>E.error("[plane-link] spend-caps push rejected:",C?.message||C));return}if(T.ch==="ctl"&&T.type==="transferred"&&Array.isArray(T.tracks)){Promise.resolve().then(()=>a?.(T.tracks)).catch(C=>E.error("[plane-link] transfer notice failed:",C?.message||C));return}if(T.ch==="events"&&T.type==="ack"&&Array.isArray(T.ids)){Zd(T.ids);return}if(T.ch==="events"&&T.type==="nack"&&Array.isArray(T.ids)){ny(T.ids,T.reason,T.permanent===!0,T.retryAfterMs??null);return}if(T.ch==="control"&&T.type==="http_open"){z$(T).catch(C=>st({v:at,ch:"control",type:"http_end",reqId:T.reqId,error:C?.message||"tunnel error"}));return}if(T.ch==="control"&&T.type==="http_cancel"){Aa.get(T.reqId)?.abort();return}if(T.ch==="attach"&&T.type==="open"){let C=new Bw(q+T.path,Array.isArray(T.protocols)?T.protocols:[],T.host?{headers:{host:T.host}}:void 0);F.set(T.streamId,C),R.set(T.streamId,[]),C.on("open",()=>{let ve=R.get(T.streamId)||[];R.delete(T.streamId);for(let{buf:$e,bin:Ue}of ve)C.send($e,{binary:Ue})}),C.on("message",(ve,$e)=>{let Ue=$e?ve:Buffer.from(String(ve));st({v:at,ch:"attach",type:"data",streamId:T.streamId,data:Ue.toString("base64"),...$e?{bin:!0}:{}})});let V=()=>{R.delete(T.streamId),F.delete(T.streamId)&&st({v:at,ch:"attach",type:"close",streamId:T.streamId})};C.on("close",V),C.on("error",V);return}if(T.ch==="attach"&&T.type==="data"){let C=F.get(T.streamId);if(!C)return;let V=Buffer.from(T.data,"base64"),ve=T.bin===!0,$e=R.get(T.streamId);$e?$e.push({buf:V,bin:ve}):C.send(V,{binary:ve});return}if(T.ch==="attach"&&T.type==="close"){let C=F.get(T.streamId);F.delete(T.streamId),R.delete(T.streamId),C?.close();return}});let M=()=>{k&&E.log("[plane-link] disconnected from plane"),k=!1,Me&&(clearInterval(Me),Me=null),Jt&&(clearInterval(Jt),Jt=null),re.clear(),ce={},wt=[],y&&(clearTimeout(y),y=null),Xe.clear();for(let C of F.values())C.close();F.clear(),R.clear();for(let C of Aa.values())C.abort();if(Aa.clear(),O||Fe)return;we==null&&(we=Date.now());let D=Date.now()-we<JF,T=Math.max(D?XF+Math.random()*ZF:le,ae-Date.now());Fe=setTimeout(()=>{Fe=null,cy()},T),Fe.unref?.(),D||(le=Math.min(le*2,dE))};m.on("close",M),m.on("error",D=>{let T=/Unexpected server response: (\d+)/.exec(D?.message||"")?.[1];(T==="401"||T==="403")&&Date.now()-Fc>=mE&&(Fc=Date.now(),E.error(`[plane-link] plane refused this node's credentials (HTTP ${T}) \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)`)),M()})}return lt.on("event",iy),L$().then(cy),{stop:()=>{O=!0,lt.off("event",iy),Fe&&clearTimeout(Fe),Lt&&clearTimeout(Lt),Me&&clearInterval(Me),Jt&&clearInterval(Jt),y&&clearTimeout(y),st({v:at,ch:"ctl",type:"bye",reason:"shutdown"}),m?.close()},status:()=>({connected:k,nodeId:n,cursor:{...he},inflight:re.size,...sy()}),_tick:()=>{oy(),Qd()}}}kl();var cd=(()=>{try{return lU(import.meta.url)("../package.json").version||null}catch{return null}})(),uU=process.env.BOSTRAT_STATE_DIR||it.join(process.env.HOME||iU.tmpdir(),".bostrat"),bE=it.join(uU,"self-update"),ld=it.join(bE,"status.json"),dU=1440*6e4,fU=6e4,pc=new Set(["npm-global","git-checkout"]);function kE({kind:e=Ho(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:pc.has(e)?e==="git-checkout"||pU(t)?"auto":"prompt":"off"}function pU(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Dt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function mU({kind:e,to:t,from:n=cd,statusFile:r=ld,logFile:o=it.join(bE,"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||it.resolve(it.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!pc.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,b="")=>`write_status ${Dt(g)} ${b}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Dt(i)}`,p=`
|
|
594
608
|
health() {
|
|
595
609
|
for i in $(seq 1 45); do
|
|
596
610
|
sleep 1
|
|
@@ -601,22 +615,22 @@ health() {
|
|
|
601
615
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
602
616
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
603
617
|
set -u
|
|
604
|
-
FROM=${
|
|
605
|
-
TO=${
|
|
606
|
-
STATUS_FILE=${
|
|
618
|
+
FROM=${Dt(n??"")}
|
|
619
|
+
TO=${Dt(t)}
|
|
620
|
+
STATUS_FILE=${Dt(r)}
|
|
607
621
|
STARTED_AT=${Number(u)}
|
|
608
622
|
write_status() { # <status> [error]
|
|
609
623
|
local err="\${2:-}"
|
|
610
624
|
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
611
625
|
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
612
|
-
"$1" "$FROM" "$TO" ${
|
|
626
|
+
"$1" "$FROM" "$TO" ${Dt(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
613
627
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
614
628
|
}
|
|
615
629
|
${p}
|
|
616
|
-
`;if(e==="npm-global"){let g=a?
|
|
617
|
-
`:"",
|
|
630
|
+
`;if(e==="npm-global"){let g=a?it.join(a,"bin","npm"):"npm",b=a?`export PATH=${Dt(it.join(a,"bin"))}:"$PATH"
|
|
631
|
+
`:"",v=a?` --prefix ${Dt(a)}`:"",x=I=>`${Dt(g)} install -g "bostrat-node@${I}" --no-audit --no-fund${v}`;return h+b+`
|
|
618
632
|
${f("updating",'"" ')}
|
|
619
|
-
if ! ${
|
|
633
|
+
if ! ${x("$TO")}; then
|
|
620
634
|
${f("failed",'"npm install failed"')}
|
|
621
635
|
exit 1
|
|
622
636
|
fi
|
|
@@ -631,14 +645,14 @@ if health; then
|
|
|
631
645
|
fi
|
|
632
646
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
633
647
|
${f("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
634
|
-
if [ -n "$FROM" ] && ${
|
|
648
|
+
if [ -n "$FROM" ] && ${x("$FROM")} && ${d} && health; then
|
|
635
649
|
${f("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
636
650
|
exit 1
|
|
637
651
|
fi
|
|
638
652
|
${f("failed",'"agent did not answer after the update AND the rollback"')}
|
|
639
653
|
exit 1
|
|
640
|
-
`}let w=g=>`( cd ${
|
|
641
|
-
LIVE=${
|
|
654
|
+
`}let w=g=>`( cd ${Dt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return h+`
|
|
655
|
+
LIVE=${Dt(l)}
|
|
642
656
|
TMP_SCRIPT="$(mktemp)"
|
|
643
657
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
644
658
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
@@ -655,64 +669,65 @@ if [ -n "$FROM" ] && ${w("$FROM")}; then
|
|
|
655
669
|
fi
|
|
656
670
|
${f("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
657
671
|
exit 1
|
|
658
|
-
`}async function lc(e=Yu){try{return JSON.parse(await Hr.readFile(e,"utf8"))}catch{return null}}var oF=new Set(["updating","restarting","rolling-back"]),Ju=e=>!!(e&&oF.has(e.status));async function Xu({to:e,from:t=zu,kind:n=Lo(),reason:r="manual",statusFile:o=Yu,spawnImpl:s=XL,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!cc.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!ic(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await lc(o);if(Ju(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 Hr.mkdir(st.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Of(await sF())||st.resolve(st.dirname(process.execPath),".."):null,f=rF({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=st.join(st.dirname(o),`update-${e}.sh`),p=st.join(st.dirname(o),"last.log");await Hr.writeFile(d,f,{mode:448}),await Hr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let h=NE.existsSync("/run/systemd/system")&&await aF("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${Bt(d)} >> ${Bt(p)} 2>&1`]];try{s(h[0],h[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await Hr.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 sF(){try{return await Hr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function aF(e){for(let t of String(process.env.PATH||"").split(st.delimiter))try{return await Hr.access(st.join(t,e),NE.constants.X_OK),!0}catch{}return!1}var iF=45*6e4;async function BE({statusFile:e=Yu,current:t=zu,now:n=Date.now,staleMs:r=iF}={}){let o=await lc(e);if(!o)return null;let s=a=>Hr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Ju(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 jE({isBusy:e,start:t=Xu,onStuck:n=null,onStarted:r=null,tickMs:o=tF,stuckMs:s=eF,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 h=u;f();let w=await t({to:h.to,from:h.from,kind:h.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...h,result:w})}catch{}};return{arm({to:p,from:h=zu,kind:w=Lo()}){return u?.to===p?!1:(f(),u={to:p,from:h,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}}}me();it();var cF=(...e)=>fetch(...e);function lF(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function hw(e=process.env){if(!lF(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 cF(`${Ie(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 oa(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await hw(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 Zu(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}})}me();it();var uF=(...e)=>fetch(...e);async function Qu(e,t,n,r=process.env){if(!(Ie(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 uF(`${Ie(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 dF(e,t=process.env){return Qu("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function LE({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await dF(t,o):await oa(e,o),a=await Qu("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function ww(e,t=process.env){let n=await oa(e,t),r=await Qu("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function gw(e,t=process.env){return(await Qu("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function FE(e,t,n,r=process.env){let{blueprint:o,items:s}=await ww(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}}me();it();var fF=(...e)=>fetch(...e);async function pF(e,t,n,r=process.env){if(!(Ie(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 fF(`${Ie(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var UE=72,yw=160;async function HE(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>UE)throw new Error(`the title is the choice as a LABEL \u2014 ${UE} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${yw} chars) a reader can act on without opening the record`);if(c.length>yw)throw new Error(`the summary is one sentence \u2014 ${yw} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await $(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function WE(e,t=process.env){return pF("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}ih();me();import hc from"node:fs/promises";import cd from"node:path";import rU from"node:os";cr();import mF from"node:fs/promises";import qE from"node:path";import hF from"node:os";var KE=()=>qE.join(process.env.BOSTRAT_STATE_DIR||qE.join(process.env.HOME||hF.tmpdir(),".bostrat"),"integrations.json");async function nr(){try{return JSON.parse(await mF.readFile(KE(),"utf8"))||{}}catch{return{}}}var ed=["linked","prOpened","prMerged","create"],bw=["prompt","auto","off"],wF={create:["prompt","off"]},GE=e=>wF[e]??bw;function td(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of ed){let s=GE(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 Fo(){let e=await nr(),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:td(e),defaultTeam:n}:null}async function nd(){let e=await nr(),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:td(e)}}async function kw(){return td(await nr())}async function VE(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${ed.join(", ")}`);for(let[r,o]of t){if(!ed.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${ed.join(", ")}`);let s=GE(r);if(!s.includes(o))throw new Error(s.length<bw.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${bw.join(", ")}`)}let n=await nr();return n.ticketFlow={...td(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Tt(KE(),n,{mode:384}),n.ticketFlow}async function uc(){let e=(await nr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function gF(){let e=(await nr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var yF={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function rd(){let t=(await nr()).voiceChat||{},n=t.provider||"openai",r=yF[n];if(!r)return null;let o=n==="openai"?await gF():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function zE(){let e=(await nr()).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 Uo(){let e=(await nr()).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}}me();ur();vr();Ks();import fc from"node:fs/promises";import rA from"node:path";import WF from"node:os";import{randomUUID as oA}from"node:crypto";var vw={};Pd(vw,{createComment:()=>_F,findIssueUrl:()=>vF,issueStates:()=>RF,mdToAdf:()=>XE,moveIssue:()=>OF,parseIssueRef:()=>xF,resolveIssue:()=>SF,uploadImage:()=>IF,upsertCard:()=>TF,whoami:()=>xw});var sa=e=>String(e.baseUrl||"").replace(/\/$/,""),JE=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ho(e,t,n,r){let o=await fetch(`${sa(e)}${n}`,{method:t,headers:{authorization:JE(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 bF=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,kF=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function xF(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(bF);return r?r[1].toUpperCase():null}let n=t.match(kF);return n?n[1]:null}function vF(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 SF(e,t){let n;try{n=await Ho(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:`${sa(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function TF(e,t,{url:n,title:r,subtitle:o}){await Ho(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 EF=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function AF(e){let t=[],n=0;for(let r of e.matchAll(EF)){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 XE(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
659
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
672
|
+
`}async function mc(e=ld){try{return JSON.parse(await Gr.readFile(e,"utf8"))}catch{return null}}var hU=new Set(["updating","restarting","rolling-back"]),ud=e=>!!(e&&hU.has(e.status));async function dd({to:e,from:t=cd,kind:n=Ho(),reason:r="manual",statusFile:o=ld,spawnImpl:s=cU,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!pc.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!fc(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await mc(o);if(ud(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 Gr.mkdir(it.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Xf(await wU())||it.resolve(it.dirname(process.execPath),".."):null,f=mU({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=it.join(it.dirname(o),`update-${e}.sh`),p=it.join(it.dirname(o),"last.log");await Gr.writeFile(d,f,{mode:448}),await Gr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let h=yE.existsSync("/run/systemd/system")&&await gU("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${Dt(d)} >> ${Dt(p)} 2>&1`]];try{s(h[0],h[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await Gr.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 wU(){try{return await Gr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function gU(e){for(let t of String(process.env.PATH||"").split(it.delimiter))try{return await Gr.access(it.join(t,e),yE.constants.X_OK),!0}catch{}return!1}var yU=45*6e4;async function xE({statusFile:e=ld,current:t=cd,now:n=Date.now,staleMs:r=yU}={}){let o=await mc(e);if(!o)return null;let s=a=>Gr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(ud(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 vE({isBusy:e,start:t=dd,onStuck:n=null,onStarted:r=null,tickMs:o=fU,stuckMs:s=dU,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 h=u;f();let w=await t({to:h.to,from:h.from,kind:h.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...h,result:w})}catch{}};return{arm({to:p,from:h=cd,kind:w=Ho()}){return u?.to===p?!1:(f(),u={to:p,from:h,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}}}ge();ct();var bU=(...e)=>fetch(...e);function kU(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Lw(e=process.env){if(!kU(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 bU(`${Oe(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 ua(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Lw(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 fd(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}})}ge();ct();var xU=(...e)=>fetch(...e);async function pd(e,t,n,r=process.env){if(!(Oe(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 xU(`${Oe(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 vU(e,t=process.env){return pd("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function SE({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await vU(t,o):await ua(e,o),a=await pd("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Fw(e,t=process.env){let n=await ua(e,t),r=await pd("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Uw(e,t=process.env){return(await pd("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function TE(e,t,n,r=process.env){let{blueprint:o,items:s}=await Fw(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}}ge();ct();var SU=(...e)=>fetch(...e);async function TU(e,t,n,r=process.env){if(!(Oe(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 SU(`${Oe(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var AE=72,Hw=160;async function EE(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>AE)throw new Error(`the title is the choice as a LABEL \u2014 ${AE} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${Hw} chars) a reader can act on without opening the record`);if(c.length>Hw)throw new Error(`the summary is one sentence \u2014 ${Hw} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await $(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function _E(e,t=process.env){return TU("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Oh();ge();import kc from"node:fs/promises";import vd from"node:path";import m4 from"node:os";fr();import AU from"node:fs/promises";import IE from"node:path";import EU from"node:os";var $E=()=>IE.join(process.env.BOSTRAT_STATE_DIR||IE.join(process.env.HOME||EU.tmpdir(),".bostrat"),"integrations.json");async function ar(){try{return JSON.parse(await AU.readFile($E(),"utf8"))||{}}catch{return{}}}var md=["linked","prOpened","prMerged","create"],Ww=["prompt","auto","off"],_U={create:["prompt","off"]},RE=e=>_U[e]??Ww;function hd(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of md){let s=RE(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function Wo(){let e=await ar(),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:hd(e),defaultTeam:n}:null}async function wd(){let e=await ar(),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:hd(e)}}async function qw(){return hd(await ar())}async function OE(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${md.join(", ")}`);for(let[r,o]of t){if(!md.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${md.join(", ")}`);let s=RE(r);if(!s.includes(o))throw new Error(s.length<Ww.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Ww.join(", ")}`)}let n=await ar();return n.ticketFlow={...hd(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Tt($E(),n,{mode:384}),n.ticketFlow}async function hc(){let e=(await ar()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function IU(){let e=(await ar()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var $U={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function gd(){let t=(await ar()).voiceChat||{},n=t.provider||"openai",r=$U[n];if(!r)return null;let o=n==="openai"?await IU():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function CE(){let e=(await ar()).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 qo(){let e=(await ar()).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}}ge();mr();Er();Xs();import gc from"node:fs/promises";import UE from"node:path";import e4 from"node:os";import{randomUUID as HE}from"node:crypto";var Kw={};tf(Kw,{createComment:()=>jU,findIssueUrl:()=>MU,issueStates:()=>UU,mdToAdf:()=>PE,moveIssue:()=>HU,parseIssueRef:()=>CU,resolveIssue:()=>NU,uploadImage:()=>LU,upsertCard:()=>PU,whoami:()=>Gw});var da=e=>String(e.baseUrl||"").replace(/\/$/,""),NE=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Go(e,t,n,r){let o=await fetch(`${da(e)}${n}`,{method:t,headers:{authorization:NE(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 RU=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,OU=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function CU(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(RU);return r?r[1].toUpperCase():null}let n=t.match(OU);return n?n[1]:null}function MU(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 NU(e,t){let n;try{n=await Go(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:`${da(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function PU(e,t,{url:n,title:r,subtitle:o}){await Go(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 DU=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function BU(e){let t=[],n=0;for(let r of e.matchAll(DU)){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 PE(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
673
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...BU(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 jU(e,t,n){let r=await Go(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:PE(n)});return r?.id?`${da(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function Gw(e){let t=await Go(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(da(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function LU(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(`${da(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:NE(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??`${da(e)}/rest/api/3/attachment/content/${c.id}`}var FU={new:"unstarted",indeterminate:"started",done:"completed"},ME=e=>FU[e?.key]??"started";async function UU(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Go(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Go(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:ME(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:ME(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function HU(e,t,n){await Go(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var Jw={};tf(Jw,{createComment:()=>JU,createIssue:()=>Yw,createProject:()=>zw,findIssueUrl:()=>VU,issueStates:()=>ZU,moveIssue:()=>QU,parseIssueRef:()=>KU,resolveIssue:()=>zU,teams:()=>fa,uploadImage:()=>XU,upsertCard:()=>YU,whoami:()=>Vw});var WU=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function On(e,t,n={}){let r=await fetch(WU(),{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 qU=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,GU=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function KU(e){let t=String(e||"").trim(),n=t.match(qU);if(n)return n[1].toUpperCase();let r=t.match(GU);return r?r[1]:null}function VU(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 zU(e,t){let r=(await On(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 YU(e,t,{url:n,title:r,subtitle:o}){let s=await On(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 JU(e,t,n){let r=await On(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 Vw(e){let t=await On(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 XU(e,{filename:t,contentType:n,buffer:r}){let o=await On(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
660
674
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
661
675
|
success
|
|
662
676
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
663
677
|
}
|
|
664
|
-
}`,{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 UF(e,t){let n=await _n(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 HF(e,t,n){if(!(await _n(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 aa(e){return((await _n(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var ZE=255;async function Tw(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>ZE,a=s?`${o.slice(0,ZE-1).trimEnd()}\u2026`:o,i=await _n(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 Ew(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await _n(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 sA={linear:{api:Aw,config:Fo},jira:{api:vw,config:nd}};async function Mw(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(sA)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Wo(e){let t=sA[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Nw(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 aA=()=>process.env.BOSTRAT_STATE_DIR||rA.join(process.env.HOME||WF.tmpdir(),".bostrat"),Rw=()=>rA.join(aA(),"tickets.json"),iA=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),od=e=>`${iA()}/track/${e}`,Wr=null,_w=null,Iw=Promise.resolve();async function mc(){return Wr||(_w??=fc.readFile(Rw(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Wr=e,!t&&Ne()){let{records:n,source:r}=await et("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;Wr[s]=i}Object.keys(Wr).length&&await Ow().catch(()=>{})}}return Wr}).finally(()=>_w=null),_w)}async function Ow(){let e=JSON.stringify({tickets:Wr},null,2),t=`${Rw()}.${process.pid}.tmp`;await fc.mkdir(aA(),{recursive:!0}),await fc.writeFile(t,e),await fc.rename(t,Rw())}async function qr(e=null){if(Iw=Iw.then(Ow,Ow),await Iw.catch(()=>{}),!e)return;let t=Wr?.[e];t?wt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Vt("tickets",e).catch(()=>{})}async function In(e){return(await mc())[e]??null}async function cA(){return{...await mc()}}async function lA(){return(await Mw()).length>0}async function ia(e,t,{source:n="api"}={}){let r=await Mw();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 mc())[e.id]=c,await qr(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 pc(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 dc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await fA(e.id,c,"linked"),c}async function Pw(e){let t=await mc();return t[e]?(delete t[e],await qr(e),!0):!1}var Cw=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function uA(e,t){if(!e.track?.cwd)return null;let n=xr(await F({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function qF(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Cw(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Cw(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function KF(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(Cw(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function pc(e,t){let n=await Wo(t);if(!n)return;let r=await uA(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:od(e),title:`Bostrat Track: ${t.track.name}`,subtitle:KF(t,r)})}function sd(e){let t=new URL(iA());return`${t.protocol}//${e}.${t.host}/`}var GF={linked:"linked",opened:"prOpened",merged:"prMerged"};function VF(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 dA(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await qr(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 fA(e,t,n){let r=await Wo(t);if(!r)return;let o=r.cfg.ticketFlow?.[GF[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Nw(s,a),c=VF(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 dA(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:oA(),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 pA(e){let t=await In(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Wo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Nw(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await qr(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 Dw(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await In(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Wo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Nw(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 qr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await dA(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function mA(e,t){let n=await In(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Wo(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 hA(e,t,n){if(!t)throw new Error("offerId required");let r=await F({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 Dw(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 QE=50;async function wA(){let e=await Fo();if(!e)throw new Error("no ticketing integration configured");return aa(e)}function zF(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>QE)throw new Error(`too many issues (${n.length}); cap is ${QE} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function YF(e,t,n,r){let o=r??await aa(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 In(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 gA(e,t){let n=await Fo();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=zF(t),o=await aa(n),s=await YF(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=oA();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 yA(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await F({id:e.id,cwd:e.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $({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 Fo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await aa(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let h=await Tw(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await Ew(a,{teamId:c.id,title:h.title,description:h.description,projectId:f,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await $({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(h=>h.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await In(e.id)&&(p=await ia(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function dc(e,t){let n=await Wo(e);n&&await n.api.createComment(n.cfg,e,t)}async function JF(e,t,n){let r=await Wo(e);if(!r)return null;let o=await Yn(t);if(!o)return null;let s=tn(o,n);if(!s)return null;let a=await fc.readFile(Wt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var eA=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),$w=new Map;function XF(e,t){if(eA()<=0)return pc(e,t);if($w.has(e))return;let n=setTimeout(()=>{$w.delete(e),In(e).then(r=>r?pc(e,r):null).catch(()=>{})},eA());n.unref?.(),$w.set(e,n)}var tA=new Map;async function nA(e,t,n){let r=tA.get(e)??0;Date.now()-r<600*1e3||(tA.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 ZF(e,{resolveTrack:t=null}={}){let n=await In(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Mw())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 ia(s,o,{source:"chat"})}catch(a){await nA(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 qr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await qr(e.trackId),await pc(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 qr(e.trackId),await pc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await uA(n,e.trackId).catch(()=>null):null;await dc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
678
|
+
}`,{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 ZU(e,t){let n=await On(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 QU(e,t,n){if(!(await On(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 fa(e){return((await On(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var DE=255;async function zw(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>DE,a=s?`${o.slice(0,DE-1).trimEnd()}\u2026`:o,i=await On(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 Yw(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await On(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 WE={linear:{api:Jw,config:Wo},jira:{api:Kw,config:wd}};async function rg(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(WE)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Ko(e){let t=WE[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function og(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 qE=()=>process.env.BOSTRAT_STATE_DIR||UE.join(process.env.HOME||e4.tmpdir(),".bostrat"),eg=()=>UE.join(qE(),"tickets.json"),GE=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),yd=e=>`${GE()}/track/${e}`,Kr=null,Xw=null,Zw=Promise.resolve();async function bc(){return Kr||(Xw??=gc.readFile(eg(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Kr=e,!t&&je()){let{records:n,source:r}=await rt("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;Kr[s]=i}Object.keys(Kr).length&&await tg().catch(()=>{})}}return Kr}).finally(()=>Xw=null),Xw)}async function tg(){let e=JSON.stringify({tickets:Kr},null,2),t=`${eg()}.${process.pid}.tmp`;await gc.mkdir(qE(),{recursive:!0}),await gc.writeFile(t,e),await gc.rename(t,eg())}async function Vr(e=null){if(Zw=Zw.then(tg,tg),await Zw.catch(()=>{}),!e)return;let t=Kr?.[e];t?gt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Xt("tickets",e).catch(()=>{})}async function Cn(e){return(await bc())[e]??null}async function KE(){return{...await bc()}}async function VE(){return(await rg()).length>0}async function pa(e,t,{source:n="api"}={}){let r=await rg();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 bc())[e.id]=c,await Vr(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 yc(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 wc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await JE(e.id,c,"linked"),c}async function sg(e){let t=await bc();return t[e]?(delete t[e],await Vr(e),!0):!1}var ng=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function zE(e,t){if(!e.track?.cwd)return null;let n=Ar(await W({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function t4(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${ng(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${ng(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function n4(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(ng(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function yc(e,t){let n=await Ko(t);if(!n)return;let r=await zE(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:yd(e),title:`Bostrat Track: ${t.track.name}`,subtitle:n4(t,r)})}function bd(e){let t=new URL(GE());return`${t.protocol}//${e}.${t.host}/`}var r4={linked:"linked",opened:"prOpened",merged:"prMerged"};function o4(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 YE(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Vr(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 JE(e,t,n){let r=await Ko(t);if(!r)return;let o=r.cfg.ticketFlow?.[r4[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=og(s,a),c=o4(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 YE(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:HE(),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 XE(e){let t=await Cn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Ko(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=og(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Vr(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 ag(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Cn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Ko(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=og(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 Vr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await YE(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function ZE(e,t){let n=await Cn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Ko(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 QE(e,t,n){if(!t)throw new Error("offerId required");let r=await W({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 ag(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 BE=50;async function e_(){let e=await Wo();if(!e)throw new Error("no ticketing integration configured");return fa(e)}function s4(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>BE)throw new Error(`too many issues (${n.length}); cap is ${BE} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function a4(e,t,n,r){let o=r??await fa(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 Cn(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 t_(e,t){let n=await Wo();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=s4(t),o=await fa(n),s=await a4(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=HE();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 n_(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await W({id:e.id,cwd:e.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await Wo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await fa(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let h=await zw(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await Yw(a,{teamId:c.id,title:h.title,description:h.description,projectId:f,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await $({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(h=>h.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await Cn(e.id)&&(p=await pa(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function wc(e,t){let n=await Ko(e);n&&await n.api.createComment(n.cfg,e,t)}async function i4(e,t,n){let r=await Ko(e);if(!r)return null;let o=await Qn(t);if(!o)return null;let s=sn(o,n);if(!s)return null;let a=await gc.readFile(Gt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var jE=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Qw=new Map;function c4(e,t){if(jE()<=0)return yc(e,t);if(Qw.has(e))return;let n=setTimeout(()=>{Qw.delete(e),Cn(e).then(r=>r?yc(e,r):null).catch(()=>{})},jE());n.unref?.(),Qw.set(e,n)}var LE=new Map;async function FE(e,t,n){let r=LE.get(e)??0;Date.now()-r<600*1e3||(LE.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 l4(e,{resolveTrack:t=null}={}){let n=await Cn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await rg())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 pa(s,o,{source:"chat"})}catch(a){await FE(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 Vr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Vr(e.trackId),await yc(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 Vr(e.trackId),await yc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await zE(n,e.trackId).catch(()=>null):null;await wc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
665
679
|
|
|
666
|
-
Agent cost for this Track: ${
|
|
680
|
+
Agent cost for this Track: ${t4(o)}.`:"")),await JE(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await i4(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await wc(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
667
681
|
|
|
668
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
669
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
670
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
682
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await wc(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 wc(n,`**Bostrat** \u2014 live preview **${r.name}**: ${bd(r.slug)}
|
|
683
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await c4(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await FE(e.trackId,s?.cwd??null,o)}}function r_({resolveTrack:e}){lt.on("event",t=>{l4(t,{resolveTrack:e}).catch(()=>{})})}async function o_(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await rt("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await bc(),r=0;for(let[o,s]of Object.entries(n))await gt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}xo();Xs();import u4 from"ws";var s_=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function kd(e,t,n={}){let r=await fetch(`${s_()}/${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 ig=(e,t)=>kd("chat.postMessage",e,t),Vo=(e,t)=>kd("chat.update",e,t);async function kt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function d4(e,t,n={}){let r=await fetch(`${s_()}/${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 a_(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await d4("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 kd("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var f4=2200;function i_({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 kd("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new u4(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},p=!1,h=setTimeout(()=>{p=!0,d()},f4);Promise.resolve().then(()=>t(f)).then(w=>{clearTimeout(h),!p&&(p=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(h),p||(p=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var u_=()=>process.env.BOSTRAT_STATE_DIR||vd.join(process.env.HOME||m4.tmpdir(),".bostrat"),fg=()=>vd.join(u_(),"slack.json"),xd=null,cg=null,lg=Promise.resolve();async function mg(){return xd||(cg??=kc.readFile(fg(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>xd=e).finally(()=>cg=null),cg)}async function c_(){let e=JSON.stringify({threads:xd},null,2),t=`${fg()}.${process.pid}.tmp`;await kc.mkdir(u_(),{recursive:!0}),await kc.writeFile(t,e),await kc.rename(t,fg())}async function un(){lg=lg.then(c_,c_),await lg.catch(()=>{})}async function hg(e){return(await mg())[e]??null}async function h4(e,t){let n=await mg();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var Se=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function xc(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function w4(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return xc(n.replace(/\n+/g," "),t)}function d_(e){let t=`*${Se(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${Se(e.repoLabel)} \xB7 \`${Se(e.branch||"?")}\` \xB7 lead *${Se(e.lead)}*`,r=e.kickoff?`> ${Se(xc(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
684
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${yd(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function pg(e,t){await Vo(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:d_(t)})}async function Yr(e,t,n,r=void 0){return ig(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var l_=new Set;function zr(e){l_.add(e),e.catch(()=>{}).finally(()=>l_.delete(e))}var Be=e=>({response_type:"ephemeral",text:e}),g4="*/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._",y4=["claude","cursor","codex"];function ug(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 dg=(e,t)=>Be(`No live Track matches \u201C${Se(t)}\u201D. Live: ${e.map(n=>`\`${Se(n.name)}\``).join(" \xB7 ")||"none"}`);async function f_(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 kt(o,{...Be(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await kt(o,{...Be("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?vd.basename(c.base):"fresh workspace"),lead:"claude"},u=await ig(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:d_(l),unfurl_links:!1});l.ts=u.ts,(await mg())[i.id]=l,await un(),await kt(o,{...Be(`\u2713 *${Se(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await kt(o,{...Be(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function b4(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await hg(n.id);s&&(s.archivedAt=Date.now(),await un(),await pg(t,s).catch(()=>{})),await kt(r,{...Be(`\u2713 *${Se(n.name)}* archived.`),replace_original:!0})}catch(s){await kt(r,{...Be(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function k4(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await kt(o,{...Be(`\u2713 Handing *${Se(n.name)}* to ${Se(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await kt(o,{...Be(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function x4(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 kt(r,{...Be(`\u2713 Stopped *${Se(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await kt(r,{...Be(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function v4(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:xc(`${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 *${Se(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function S4(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?vd.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 *${Se(s.name)}* \u2014 ${Se(i)}${a.branch?` \`${Se(a.branch)}\``:""} \xB7 lead ${Se(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
671
685
|
`)+(o?`
|
|
672
|
-
_${o} archived_`:"")}async function
|
|
673
|
-
\u2713 *${
|
|
674
|
-
\u2713 _answered_`}}]})}async function
|
|
675
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
676
|
-
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function MU(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 NU=new Set(["Write","Edit","NotebookEdit"]),PU=220,DU=160;function BU(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(NU.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,PU)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=no(o.command.trim().replace(/\s+/g," ")).slice(0,DU));return r}function jU(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Jt(t.cwd,t.sessionId):t?.cwd?gf(t.cwd):null}async function RA(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=iu(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:BU(e,o)});break;case"Stop":await LU(r,o,e,i);break;default:break}return a}async function LU(e,t,n,r){let o=[],s=jU(n,t);if(s)try{o=CU(await OU.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=MU(await F(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"}\0${f.speed||""}`,p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let h of Object.keys(p.tokens))p.tokens[h]+=f.tokens[h];f.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=f.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=f.cacheWriteTtl.h1),p.messageIds.push(f.messageId),p.speed=f.speed||null,u.set(d,p)}for(let[f,d]of u){let p=f.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:mn(p,d.tokens,void 0,d.speed?{speed:d.speed}:void 0),tokens:d.tokens,...d.speed?{speed:d.speed}:{},cacheWriteTtl:d.cacheWriteTtl,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}})}Yo();lf();me();kl();wu();import{execFile as FU}from"node:child_process";import{promisify as UU}from"node:util";var HU=UU(FU),Gw=(e,t)=>HU("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4});function OA(e,t=Date.now()){return Vd(e,t)??0}function WU(e,t,n){for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="cost.recorded"||o.boxId!==t||n!=null&&o.epoch!=null&&o.epoch!==n)continue;let s=o.payload?.model;if(s&&s!=="unknown")return s}return null}async function CA(e,t,{now:n=Date.now()}={}){let r=OA(t,n),o=await F(e);return hu({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:WU(o,t.name,r)})}function qU(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=null;for(let c=n.length-1;c>=0;c--){let l=n[c];if(l.type==="turn.user"&&l.boxId===t.name&&l.actor==="user"){i=Pn(l.payload?.promptHead||l.payload?.text||"")?.category??null;break}}return{wallReason:a,fixerCategory:i}}async function KU(e,t){try{let{stdout:n}=await Gw(e,["show","--shortstat","--format=",t]),r=/(\d+) files? changed/.exec(n),o=/(\d+) insertions?\(\+\)/.exec(n),s=/(\d+) deletions?\(-\)/.exec(n);return{files:r?Number(r[1]):0,insertions:o?Number(o[1]):0,deletions:s?Number(s[1]):0}}catch{return{files:0,insertions:0,deletions:0}}}async function GU(e){try{let{stdout:t}=await Gw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function MA(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await F(e);if(a.some(p=>p.type==="code.committed"&&p.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:p}=await Gw(r,["show","-s","--format=%ct",n]),h=Number(p.trim())*1e3;Number.isFinite(h)&&h>0&&(i=h)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=OA(t,i),l=await KU(r,n),u=await $(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:await GU(r),...l,ts:i}}),f=null,d=!1;try{let p=await mS(r,n,{fixKey:Us({trackId:e.id,boxId:t.name,epoch:c}),window:Dr,context:{...qU(e,t,a,c),provider:t.agent??null}});if(p){f=await $(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:u.id,payload:p});let h=hS(p);if(h>=pS){let w=p.origins.filter(g=>g.class==="rework").slice(0,3);await $(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:f.id,payload:{sha:n,subject:p.subject,linesReworked:h,origins:w.map(g=>({key:g.key,provider:g.provider,model:g.model,lines:g.lines,ageDays:g.ageDays})),heuristic:p.heuristic,ts:i}}),d=!0}}}catch(p){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${p?.message||p}`)}return{recorded:!0,event:u,rework:f,offered:d}}async function NA(e,t,n){if(!$i.includes(n))throw new Error(`bad cause "${n}" (${$i.join("|")})`);let r=await F(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(r.some(s=>s.type==="rework.cause.answered"&&s.payload?.sha===t))throw new Error("already answered");return $(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,cause:n,level:"B",ts:Date.now()}})}me();me();vr();me();vr();Dn();function gc(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 PA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),DA=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},ld=e=>Math.round(e*1e4)/1e4;async function Vw(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=PA(),f=0,d=[];for(let p of e||[]){let h=await F(p),w=xr(h,{boxLabels:gc(p)});if(w.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let g of w.unpricedModels||[])l.add(g);DA(u,w.totalTokens);for(let[g,k]of Object.entries(w.byModel))n[g]||={usd:0,events:0,tokens:PA()},n[g].usd=ld(n[g].usd+k.usd),n[g].events+=k.events,DA(n[g].tokens,k.tokens);for(let[g,k]of Object.entries(w.byProvider))r[g]||={usd:0,events:0},r[g].usd=ld(r[g].usd+k.usd),r[g].events+=k.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,h)=>h.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:bs(),fleet:{totalUsd:ld(o),asRecordedUsd:ld(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)}`,ca=(e,t)=>String(e).padEnd(t);function BA(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];if(r.push(`fleet total ${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(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${ca(a,26)} ${ca(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(` ${ca(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(` ${ca(c.label,40)} ${ca(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))}${ca(Vr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
677
|
-
`)}me();Wp();import XU from"node:fs/promises";import ZU from"node:path";kl();vr();var ud=e=>e.payload?.ts??e.ts,VU=12e4,zr=e=>Math.round(e*1e4)/1e4,zU=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function YU(e,{since:t}){let{track:n,events:r,brief:o}=e,s=S=>ud(S)>=t,a=Pn([ri(n?.name),ri(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let E=Pn(S.payload.task);E&&i.set(S.boxId,E)}let c=new Map,l=S=>{let E=S||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((S,E)=>ud(S)-ud(E)),d=0,p=0,h=0,w={turns:0};for(let S of f){let E=l(S.boxId),R=E.segments[E.segments.length-1],B=ud(S);switch(E.lastTs!=null&&B>E.lastTs&&s(S)&&(R.activeMs+=Math.min(B-E.lastTs,VU)),E.lastTs=B,S.provider&&(E.provider=S.provider),S.type){case"turn.user":{let T=S.payload?.text;if(S.source==="hook"||!String(T||"").trim())break;let _=u(Pn(T));E.segments.push(_),s(S)&&(_.turns+=1);break}case"peer.message":{let T=S.payload?.text;if(!String(T||"").trim())break;let _=u(Pn(T));E.segments.push(_),s(S)&&(_.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let T=S.payload?.tool;T&&(R.tools[T]=(R.tools[T]||0)+1),s(S)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let{usd:T,tokens:_,model:O,unpriced:I}=Ft(S);I&&(w.turns+=1),R.usd+=T,_&&(R.tokens.input+=_.input,R.tokens.output+=_.output,R.tokens.cacheRead+=_.cacheRead,R.tokens.cacheWrite+=_.cacheWrite),R.models[O]=(R.models[O]||0)+T,S.provider&&(R.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let g=new Map,k=!1;for(let[S,E]of c)for(let R of E.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let T=R.textCat?.category??i.get(S)?.category??Ub(R.tools)?.category??a?.category??"uncategorized";g.has(T)||g.set(T,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let _=g.get(T);_.usd+=R.usd,_.activeMs+=R.activeMs,_.turns+=R.turns,_.toolCalls+=R.toolCalls;for(let[I,U]of Object.entries(R.models))_.models[I]=(_.models[I]||0)+U;let O=R.provider||E.provider;O&&(_.providers[O]=(_.providers[O]||0)+R.usd),O==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:g,handoffs:d,merged:p,failed:h,sawCursorActivity:k,unpricedTurns:w.turns}}function LA(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:g,events:k}=w;if(!k?.length){a+=1;continue}let{perCategory:S,handoffs:E,merged:R,failed:B,sawCursorActivity:T,unpricedTurns:_}=YU(w,{since:r});T&&(i=!0),c+=_;let O={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:R,subtasksFailed:B};for(let[I,U]of S){O.usd+=U.usd,O.activeMs+=U.activeMs,O.turns+=U.turns,O.categories[I]={usd:zr(U.usd),activeMs:U.activeMs};for(let[X,C]of Object.entries(U.models))O.models[X]=zr((O.models[X]||0)+C);for(let[X,C]of Object.entries(U.providers))O.providers[X]=zr((O.providers[X]||0)+C);o.has(I)||o.set(I,zU());let H=o.get(I);H.usd+=U.usd,H.activeMs+=U.activeMs,H.turns+=U.turns,H.toolCalls+=U.toolCalls;for(let[X,C]of Object.entries(U.models))H.models[X]=(H.models[X]||0)+C;for(let[X,C]of Object.entries(U.providers))H.providers[X]=(H.providers[X]||0)+C;let ae=H.byTrack.get(g?.id)||{usd:0,activeMs:0};ae.usd+=U.usd,ae.activeMs+=U.activeMs,H.byTrack.set(g?.id,ae)}(O.usd>0||O.activeMs>0||O.turns>0)&&(O.usd=zr(O.usd),O.dominantCategory=JU(O.categories),s.push(O))}let l=[...o.values()].reduce((w,g)=>w+g.usd,0),u=[...o.values()].reduce((w,g)=>w+g.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),h=[...o.entries()].map(([w,g])=>({category:w,sharePct:d>0?Math.round((f==="usd"?g.usd:g.activeMs)/d*100):0,shareBasis:f,usd:zr(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:jA(g.models),providers:jA(g.providers),topWorkstreams:[...g.byTrack.entries()].sort((k,S)=>S[1].usd-k[1].usd||S[1].activeMs-k[1].activeMs).slice(0,2).map(([k,S])=>({trackId:k,name:p.get(k),usd:zr(S.usd),activeMs:S.activeMs}))})).sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs);return s.sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:zr(l),activeMs:u,turns:[...o.values()].reduce((w,g)=>w+g.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var JU=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},jA=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,zr(n)]));var la={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function QU(e){let t=String(e||"7d");return t in la?{key:t,ms:la[t]}:{key:"7d",ms:la["7d"]}}function e4(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=Wl(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 t4(e){if(!e?.cwd)return null;try{let n=(await XU.readFile(ZU.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 FA(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}:QU(t),f=[];for(let d of e||[]){let p=await F(d),h=e4(d,p);i&&c<o&&(h=h.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:h,brief:await t4(d)})}return{window:l,...LA(f,{windowMs:u,now:c})}}Dn();var Yr=36e5,qA=24*Yr,n4=400,r4=12,Ko=e=>Math.round(e*1e4)/1e4;function zw({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 la?String(e||"7d"):"7d",i=la[a];return{key:a,since:i==null?null:r-i,until:r}}var o4=(e,t)=>e!=null&&t-e<=48*Yr?Yr:qA;function UA(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 s4(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 HA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),WA=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},a4=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ko(n)]));async function KA(e,t={}){let n=t.now??Date.now(),r=zw({...t,now:n}),o=r.since==null?qA:o4(r.since,r.until),s=I=>(r.since==null||I>=r.since)&&I<r.until,a=r.since!=null?r.until-r.since:null,i=I=>a!=null&&I>=r.since-a&&I<r.since,c={},l={},u=HA(),f=0,d=0,p=0,h=0,w=0,g=0,k=new Set,S=new Map,E=[],R=[],B=[],T=null;for(let I of e||[]){let H=(await F(I)).filter(C=>C.type==="cost.recorded");if(!H.length){I.archivedAt||B.push(I.name||I.id);continue}let ae=[];for(let C of H){let Q=C.ts??0;s(Q)?ae.push(C):i(Q)&&(g+=Ft(C).usd)}if(!ae.length)continue;let X=xr(ae,{boxLabels:gc(I)});f+=X.totalUsd,d+=X.turns,p+=X.repricedTurns||0,h+=X.asRecordedUsd||0,w+=X.unpricedTurns||0;for(let C of X.unpricedModels||[])k.add(C);WA(u,X.totalTokens);for(let[C,Q]of Object.entries(X.byModel))c[C]||={usd:0,events:0,tokens:HA()},c[C].usd=Ko(c[C].usd+Q.usd),c[C].events+=Q.events,WA(c[C].tokens,Q.tokens);for(let[C,Q]of Object.entries(X.byProvider))l[C]||={usd:0,events:0},l[C].usd=Ko(l[C].usd+Q.usd),l[C].events+=Q.events;for(let C of ae){let{usd:Q}=Ft(C);if(!Q)continue;let ve=UA(C.ts??0,o),W=S.get(ve)??{t:ve,total:0,byProvider:{}};W.total+=Q;let be=C.provider||"unknown";W.byProvider[be]=(W.byProvider[be]||0)+Q,S.set(ve,W),(T==null||ve<T)&&(T=ve)}E.push({id:I.id,name:I.name||I.id,archived:!!I.archivedAt,usd:X.totalUsd,turns:X.turns,providers:Object.keys(X.byProvider)});for(let[C,Q]of Object.entries(X.byBox))R.push({trackId:I.id,trackName:I.name||I.id,boxId:C,...Q})}let _=r.since!=null?UA(r.since,o):T,O=[];for(let I=_,U=0;I!=null&&I<r.until&&U<n4;I=s4(I,o),U++){let H=S.get(I);O.push(H?{t:I,total:Ko(H.total),byProvider:a4(H.byProvider)}:{t:I,total:0,byProvider:{}})}return E.sort((I,U)=>U.usd-I.usd),R.sort((I,U)=>U.usd-I.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:bs(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:O,fleet:{totalUsd:Ko(f),asRecordedUsd:Ko(h),turns:d,repricedTurns:p,unpricedTurns:w,unpricedModels:[...k],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:E.length,tracksSilentLive:B},prior:a!=null?{totalUsd:Ko(g),since:r.since-a,until:r.since}:null,tracks:E.slice(0,50),boxes:R.slice(0,r4)}}wu();var i4=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),c4=20,l4=40;async function GA(e,t={}){let n=t.now??Date.now(),r=zw({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await F(l),f=0,d=gc(l);for(let p of u){if(!i4.has(p.type))continue;let h=p.type==="code.rework"?p.payload?.ts??p.ts:p.ts;p.type!=="rework.cause.answered"&&!o(h??0)||(p.type==="code.committed"&&f++,s.push(p.trackId?p:{...p,trackId:l.id}))}f&&i++;for(let p of l.boxes||[])a[`${l.id}/${p.name}`]={trackName:l.name||l.id,label:d[p.name]||p.name}}let c=wS(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:Dr,decayEndDays:$m,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,c4),fixes:c.fixes.slice(0,l4)}}import u4 from"node:fs";import VA from"node:fs/promises";import yc from"node:os";import d4 from"node:path";Gm();lt();var f4=1024*1024,dd=e=>Math.round(e/f4);async function p4(e){try{let t=await VA.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:dd(r),diskTotalMb:dd(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function m4(){try{return(await zt()).filter(t=>t.isBostrat).length}catch{return null}}async function h4(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await VA.readdir(d4.join(sc,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function zA({stateDir:e=sc,now:t=Date.now}={}){let n=yc.totalmem(),[r,o,s,a]=await Promise.all([Su().catch(()=>null),p4(u4.existsSync(e)?e:yc.tmpdir()),m4(),h4()]),i=yc.cpus()?.length||null,c=yc.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?dd(n-r):null,memTotalMb:dd(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(yc.uptime())}}me();Hf();Oa();Dn();me();Dn();var w4=864e5;function g4(e,{tolerance:t=.005}={}){if(e.type!=="cost.recorded"||e.source==="vendor")return null;let n=e.payload?.tokens,r=Number(e.payload?.usd)||0;if(!(n?(n.input||0)+(n.output||0)+(n.cacheRead||0)+(n.cacheWrite??n.cacheCreation??0):0)||!r)return null;let s=e.payload?.model||"unknown",a=e.payload?.speed||void 0,i={input:n.input||0,output:n.output||0,cacheRead:n.cacheRead||0,cacheWrite:n.cacheWrite??n.cacheCreation??0},c=mn(s,i,e.ts,a?{speed:a}:void 0);if(!c)return null;let l=r-c,u=Math.abs(l)>Math.max(c*t,.001);return{model:s,speed:a??null,ts:e.ts,asRecorded:r,expected:c,delta:l,divergent:u}}async function YA(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*w4,s={},a=0,i=0,c=[];for(let u of e||[]){let f=await F(u);for(let d of f){if(!d.ts||d.ts<o)continue;let p=g4(d,{tolerance:n});if(!p)continue;a++;let h=p.speed?`${p.model} (${p.speed})`:p.model,w=s[h]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};w.rows++,w.asRecordedUsd+=p.asRecorded,w.expectedUsd+=p.expected,p.divergent&&(i++,w.divergent++,c.length<10&&c.push({track:u.name||u.id,...p}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}Em();Fl();it();Bn();import ua from"node:fs/promises";import y4 from"node:fs";import ft from"node:path";import b4 from"node:os";import{execFile as k4}from"node:child_process";import{promisify as x4}from"node:util";var v4=x4(k4),JA=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||b4.tmpdir(),".bostrat"),fd=!1,Yw=null;async function Jw(e,t){let n=await ua.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ua.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ua.mkdir(ft.dirname(t),{recursive:!0}),await ua.copyFile(e,t),!0)}var S4=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 pd(e,t,{skip:n=null}={}){let r=0,o=await ua.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(S4.has(s.name)||n?.has(s.name))continue;let a=ft.join(e,s.name);s.isDirectory()?r+=await pd(a,ft.join(t,s.name)):s.isFile()&&(r+=await Jw(a,ft.join(t,s.name))?1:0)}return r}async function XA({stateDir:e=JA,dest:t=process.env.BOSTRAT_SYNC_DIR||`${JA}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ua.mkdir(t,{recursive:!0});let o=0;o+=await Jw(ft.join(e,"tracks.json"),ft.join(t,"tracks.json"))?1:0,o+=await Jw(ft.join(e,"events.jsonl"),ft.join(t,"events.jsonl"))?1:0,o+=await pd(ft.join(e,"events"),ft.join(t,"events")),o+=await pd(ft.join(e,"memory"),ft.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await pd(ft.join(u.cwd,".bostrat"),ft.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>v4("git",["-C",t,...u]);y4.existsSync(ft.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 Wd(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||fd))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,fd&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),fd=!1,Yw=null}catch(u){fd=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
678
|
-
`).
|
|
679
|
-
`).
|
|
680
|
-
`).map(s=>s.replace(/\s+$/,"")),n=
|
|
681
|
-
`)
|
|
682
|
-
|
|
686
|
+
_${o} archived_`:"")}async function T4(e,t){let n=await qo();if(!n)return Be("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 Be("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?(zr(f_(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Be(`Starting *${Se(u)}* on ${Se(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:v4(u,i)}}case"archive":{if(!a.length)return Be("Usage: `/bostrat archive <track>`");let{match:i,live:c}=ug(await r.listTracks(),a.join(" "));return i?(zr(b4(e,n,{track:i,responseUrl:t.response_url})),Be(`Archiving *${Se(i.name)}*\u2026`)):dg(c,a.join(" "))}case"handoff":{let i=r.knownAgents??y4,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Be(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=ug(await r.listTracks(),l);return u?(zr(k4(e,n,{track:u,agent:c,responseUrl:t.response_url})),Be(`Handing *${Se(u.name)}* to ${Se(c)}\u2026`)):dg(f,l)}case"stop":{if(!a.length)return Be("Usage: `/bostrat stop <track>`");let{match:i,live:c}=ug(await r.listTracks(),a.join(" "));return i?(zr(x4(e,n,{track:i,responseUrl:t.response_url})),Be(`Stopping *${Se(i.name)}*\u2026`)):dg(c,a.join(" "))}case"list":return Be(await S4(r));default:return Be(g4)}}async function A4(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await hg(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 un(),await Vo(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Se(l||"question")}~
|
|
687
|
+
\u2713 *${Se(a)}*`}}]}).catch(()=>{})}}catch(c){await kt(n.response_url,Be(`Couldn't answer: ${c?.message||c}`))}}async function E4(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)?Rr.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,Bs(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:f?a:"dismiss",actions:f}})}catch(c){await kt(n.response_url,Be(`Couldn't act on the offer: ${c?.message||c}`))}}async function _4(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 kt(n.response_url,Be(`Couldn't act on the offer: ${i?.message||i}`))}}async function I4(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await qo();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await kt(t.response_url,{...Be(`Starting *${Se(s)}*\u2026`),replace_original:!0}),zr(f_(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"?zr(A4(e,r,t,o)):n.action_id==="bostrat-ship"?zr(E4(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&zr(_4(e,r,t,o))}async function $4(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 h4(t.channel,t.thread_ts);if(!n)return;let r=await qo();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Yr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function R4(e){return async t=>{if(t.type==="slash_commands")return T4(e,t.payload);if(t.type==="interactive"){await I4(e,t.payload);return}if(t.type==="events_api"){await $4(e,t.payload?.event);return}}}var O4=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 C4(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${Se(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:xc(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 M4(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await Vo(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Se(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Yr(e,t,r.question||"The lead needs an answer",C4(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await un()}async function N4(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 un(),await Vo(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Se(s||"question")}~
|
|
688
|
+
\u2713 _answered_`}}]})}async function P4(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 Yr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Se(xc(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await un()}async function D4(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await un();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 Vo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function B4(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 Yr(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${Se(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 un()}async function j4(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await un();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Vo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function L4(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??F4)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await a_(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${Se(o.name)}*${a}${o.group?` (group \`${Se(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${Se(a.name)}*`).join(", ");await Yr(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${Se(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function F4(e,t){let n=await Qn(e);if(!n)return null;let r=sn(n,t);if(!r)return null;let o=await kc.readFile(Gt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function U4(e,t){if(!O4.has(t.type))return;let n=await hg(t.trackId);if(!n)return;let r=await qo();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 un(),await pg(r,n);return;case"turn.agent":{if(!o.text)return;let s=w4(o.text);if(!s)return;await Yr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Se(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${yd(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return M4(r,n,t);case"prompt.answered":return N4(r,n,t);case"pr.offered":return P4(r,n,t);case"pr.offer.answered":return D4(r,n,t);case"pr.exists.offered":return B4(r,n,t);case"pr.exists.answered":return j4(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 Yr(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 un(),await pg(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Yr(r,n,`Live preview ${o.name}: ${bd(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Se(o.name)}*: ${bd(o.slug)}
|
|
689
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return L4(r,n,t,e.deps)}}function p_(e){let t={deps:e};lt.on("event",s=>{U4(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await qo()||(r&&clearInterval(r),r=null,n=i_({getToken:async()=>(await qo())?.appToken,onEnvelope:R4(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}tn();ge();hn();Lm();Fn();import H4 from"node:fs/promises";function W4(e){let t=new Map;for(let n of String(e||"").split(`
|
|
690
|
+
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function q4(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 G4=new Set(["Write","Edit","NotebookEdit"]),K4=220,V4=160;function z4(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(G4.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,K4)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=oo(o.command.trim().replace(/\s+/g," ")).slice(0,V4));return r}function Y4(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?en(t.cwd,t.sessionId):t?.cwd?Bf(t.cwd):null}async function m_(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=vu(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:z4(e,o)});break;case"Stop":await J4(r,o,e,i);break;default:break}return a}async function J4(e,t,n,r){let o=[],s=Y4(n,t);if(s)try{o=W4(await H4.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=q4(await W(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"}\0${f.speed||""}`,p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let h of Object.keys(p.tokens))p.tokens[h]+=f.tokens[h];f.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=f.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=f.cacheWriteTtl.h1),p.messageIds.push(f.messageId),p.speed=f.speed||null,u.set(d,p)}for(let[f,d]of u){let p=f.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:yn(p,d.tokens,void 0,d.speed?{speed:d.speed}:void 0),tokens:d.tokens,...d.speed?{speed:d.speed}:{},cacheWriteTtl:d.cacheWriteTtl,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}})}es();$f();xu();ge();ge();Er();ge();Er();Fn();function vc(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 h_=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),w_=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Sd=e=>Math.round(e*1e4)/1e4;async function wg(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=h_(),f=0,d=[];for(let p of e||[]){let h=await W(p),w=Ar(h,{boxLabels:vc(p)});if(w.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let g of w.unpricedModels||[])l.add(g);w_(u,w.totalTokens);for(let[g,b]of Object.entries(w.byModel))n[g]||={usd:0,events:0,tokens:h_()},n[g].usd=Sd(n[g].usd+b.usd),n[g].events+=b.events,w_(n[g].tokens,b.tokens);for(let[g,b]of Object.entries(w.byProvider))r[g]||={usd:0,events:0},r[g].usd=Sd(r[g].usd+b.usd),r[g].events+=b.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,h)=>h.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:As(),fleet:{totalUsd:Sd(o),asRecordedUsd:Sd(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 Jr=e=>`$${e.toFixed(4)}`,ma=(e,t)=>String(e).padEnd(t);function g_(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];if(r.push(`fleet total ${Jr(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 ${Jr(t.asRecordedUsd)} \xB7 current-rates total ${Jr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Jr(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${ma(a,26)} ${ma(Jr(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(` ${ma(a.name||a.id,30)} ${Jr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ma(c.label,40)} ${ma(Jr(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))}${ma(Jr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
691
|
+
`)}ge();cm();import r2 from"node:fs/promises";import o2 from"node:path";_l();Cl();Er();var Td=e=>e.payload?.ts??e.ts,X4=12e4,Vt=e=>Math.round(e*1e4)/1e4,Z4=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function Q4(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Td(x)>=t,a=Ln([ii(n?.name),ii(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let x of r)if(x.type==="subtask.delegated"&&x.boxId&&x.payload?.task){let I=Ln(x.payload.task);I&&i.set(x.boxId,I)}let c=new Map,l=x=>{let I=x||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=(x,{shape:I="initial",continuation:P=!1,text:B=null,ts:j=null}={})=>({textCat:x,shape:I,continuation:P,text:B,ts:j,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((x,I)=>Td(x)-Td(I)),d=0,p=0,h=0,w={turns:0};for(let x of f){let I=l(x.boxId),P=I.segments[I.segments.length-1],B=Td(x);switch(I.lastTs!=null&&B>I.lastTs&&s(x)&&(P.activeMs+=Math.min(B-I.lastTs,X4)),I.lastTs=B,x.provider&&(I.provider=x.provider),x.type){case"turn.user":{let j=x.payload?.text;if(x.source==="hook"||!String(j||"").trim()||x.payload?.note||Ts(j))break;let L=Ln(j),A=!!x.payload?.kind,E=A||bk(j),q=u(L,{shape:A?"injected":E?"follow-up":"novel-ask",continuation:!L&&E,text:b_(j),ts:B});I.segments.push(q),s(x)&&(q.turns+=1);break}case"peer.message":{let j=x.payload?.text;if(!String(j||"").trim())break;let L=Ln(j),A=u(L,{shape:"peer",continuation:!L,text:b_(j),ts:B});I.segments.push(A),s(x)&&(A.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let j=x.payload?.tool;j&&(P.tools[j]=(P.tools[j]||0)+1),s(x)&&(P.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:j,tokens:L,model:A,unpriced:E}=Ut(x);E&&(w.turns+=1),P.usd+=j,L&&(P.tokens.input+=L.input,P.tokens.output+=L.output,P.tokens.cacheRead+=L.cacheRead,P.tokens.cacheWrite+=L.cacheWrite),P.models[A]=(P.models[A]||0)+j,x.provider&&(P.provider=x.provider);break}case"handoff.done":s(x)&&(d+=1);break;case"subtask.merged":s(x)&&(p+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let g=new Map,b={usd:0,turns:0,byShape:{},samples:[]},v=!1;for(let[x,I]of c){let P=null;for(let B of I.segments){let j=null,L=null;if(B.textCat)j=B.textCat.category,L="text";else if(i.get(x))j=i.get(x).category,L="boxTask";else if(B.continuation&&P)j=P,L="inherited";else{let F=yk(B.tools);F?(j=F.category,L="toolMix"):a&&(j=a.category,L="hint")}if(j&&(P=j),!(B.usd>0||B.activeMs>0||B.turns>0||B.toolCalls>0))continue;let E=j??"uncategorized";g.has(E)||g.set(E,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let q=g.get(E);q.usd+=B.usd,q.activeMs+=B.activeMs,q.turns+=B.turns,q.toolCalls+=B.toolCalls,L&&(q.provenance[L]=(q.provenance[L]||0)+B.usd);for(let[F,R]of Object.entries(B.models))q.models[F]=(q.models[F]||0)+R;let z=B.provider||I.provider;z&&(q.providers[z]=(q.providers[z]||0)+B.usd),z==="cursor"&&B.usd===0&&(v=!0),j||(b.usd+=B.usd,b.turns+=B.turns,b.byShape[B.shape]=(b.byShape[B.shape]||0)+B.usd,B.usd>0&&B.text&&b.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:B.shape,usd:Vt(B.usd),ts:B.ts,text:B.text}))}}return{perCategory:g,uncategorized:b,handoffs:d,merged:p,failed:h,sawCursorActivity:v,unpricedTurns:w.turns}}function gg(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,l={usd:0,turns:0,byShape:{},samples:[]};for(let b of e||[]){let{track:v,events:x}=b;if(!x?.length){a+=1;continue}let{perCategory:I,uncategorized:P,handoffs:B,merged:j,failed:L,sawCursorActivity:A,unpricedTurns:E}=Q4(b,{since:r});A&&(i=!0),c+=E,l.usd+=P.usd,l.turns+=P.turns;for(let[z,F]of Object.entries(P.byShape))l.byShape[z]=(l.byShape[z]||0)+F;l.samples.push(...P.samples);let q={trackId:v?.id,name:v?.name||v?.id,archived:!!v?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:B,subtasksMerged:j,subtasksFailed:L};for(let[z,F]of I){q.usd+=F.usd,q.activeMs+=F.activeMs,q.turns+=F.turns,q.categories[z]={usd:Vt(F.usd),activeMs:F.activeMs};for(let[X,K]of Object.entries(F.models))q.models[X]=Vt((q.models[X]||0)+K);for(let[X,K]of Object.entries(F.providers))q.providers[X]=Vt((q.providers[X]||0)+K);o.has(z)||o.set(z,Z4());let R=o.get(z);R.usd+=F.usd,R.activeMs+=F.activeMs,R.turns+=F.turns,R.toolCalls+=F.toolCalls;for(let[X,K]of Object.entries(F.models))R.models[X]=(R.models[X]||0)+K;for(let[X,K]of Object.entries(F.providers))R.providers[X]=(R.providers[X]||0)+K;for(let[X,K]of Object.entries(F.provenance))R.provenance[X]=(R.provenance[X]||0)+K;let G=R.byTrack.get(v?.id)||{usd:0,activeMs:0};G.usd+=F.usd,G.activeMs+=F.activeMs,R.byTrack.set(v?.id,G)}(q.usd>0||q.activeMs>0||q.turns>0)&&(q.usd=Vt(q.usd),q.dominantCategory=n2(q.categories),s.push(q))}let u=[...o.values()].reduce((b,v)=>b+v.usd,0),f=[...o.values()].reduce((b,v)=>b+v.activeMs,0),d=u>0?"usd":"time",p=d==="usd"?u:f,h=new Map((e||[]).map(b=>[b.track?.id,b.track?.name||b.track?.id])),w=[...o.entries()].map(([b,v])=>({category:b,sharePct:p>0?Math.round((d==="usd"?v.usd:v.activeMs)/p*100):0,shareBasis:d,usd:Vt(v.usd),activeMs:v.activeMs,turns:v.turns,toolCalls:v.toolCalls,models:Sc(v.models),providers:Sc(v.providers),provenance:Sc(v.provenance),topWorkstreams:[...v.byTrack.entries()].sort((x,I)=>I[1].usd-x[1].usd||I[1].activeMs-x[1].activeMs).slice(0,2).map(([x,I])=>({trackId:x,name:h.get(x),usd:Vt(I.usd),activeMs:I.activeMs}))})).sort((b,v)=>v.usd-b.usd||v.activeMs-b.activeMs);s.sort((b,v)=>v.usd-b.usd||v.activeMs-b.activeMs);let g={};for(let b of o.values())for(let[v,x]of Object.entries(b.provenance))g[v]=(g[v]||0)+x;return{since:r,capturedAt:n,totalUsd:Vt(u),activeMs:f,turns:[...o.values()].reduce((b,v)=>b+v.turns,0),bullets:w,workstreams:s,provenance:Sc(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:Vt(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Sc(l.byShape),top:l.samples.sort((b,v)=>v.usd-b.usd).slice(0,e2)}}}}var e2=25,y_=160,b_=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>y_?`${t.slice(0,y_).trimEnd()}\u2026`:t},t2=10;function k_(e,{thresholdPct:t=t2}={}){let n=e?.gaps?.uncategorized||{usd:0,turns:0},r=e?.totalUsd||0,o=n.usd||0,s=r>0?o/r*100:0,a=s<=t,i=a?[]:[`${s.toFixed(1)}% of the last 7d ($${o.toFixed(2)} of $${r.toFixed(2)}) is uncategorized (threshold ${t}%) \u2014 run \`npm run cost -- --uncategorized\` and add classifier table lines for the top asks`];return{ok:a,sharePct:Math.round(s*10)/10,thresholdPct:t,usd:Vt(o),totalUsd:Vt(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var n2=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},Sc=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Vt(n)]));var Yo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function s2(e){let t=String(e||"7d");return t in Yo?{key:t,ms:Yo[t]}:{key:"7d",ms:Yo["7d"]}}function a2(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=Jl(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 i2(e){if(!e?.cwd)return null;try{let n=(await r2.readFile(o2.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 yg(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}:s2(t),f=[];for(let h of e||[]){let w=await W(h),g=a2(h,w);i&&c<o&&(g=g.filter(b=>(b.ts??0)<=c)),f.push({track:h,events:g,brief:await i2(h)})}let d=gg(f,{windowMs:u,now:c}),p=l==="7d"&&c===o?d:gg(f,{windowMs:Yo["7d"],now:o});return{window:l,...d,categorization:k_(p)}}var Tc=e=>`$${Number(e||0).toFixed(2)}`,x_=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},zo=(e,t)=>String(e).padEnd(t);function v_(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${Tc(e.totalUsd)} \xB7 ${x_(e.activeMs)} active \xB7 ${e.turns} turn(s) \xB7 as of ${new Date(e.capturedAt).toISOString()}`);let n=e.provenance||{};e.totalUsd>0&&Object.keys(n).length&&t.push("labeled by: "+Object.entries(n).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Math.round(i/e.totalUsd*100)}%`).join(" \xB7 ")),t.push(""),t.push("WHERE IT WENT");for(let a of e.bullets||[])t.push(` ${zo(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${zo(a.category,15)} ${zo(Tc(a.usd),10)} ${zo(x_(a.activeMs),6)} ${a.turns} turn(s)`+(a.topWorkstreams?.length?` \xB7 ${a.topWorkstreams.map(i=>i.name).join(", ")}`:""));let r=e.gaps||{};r.tracksWithoutEvents&&t.push(`${r.tracksWithoutEvents} track(s) have no attributed events yet`),r.cursorCostUnknown&&t.push("cursor time counted, cost unavailable"),r.unpricedTurns&&t.push(`WARNING: ${r.unpricedTurns} turn(s) unpriced \u2014 total under-reports`);let o=e.categorization;if(o){t.push(""),t.push((o.ok?"categorization ok: ":"CATEGORIZATION ALARM: ")+`${o.sharePct}% of the last 7d uncategorized (threshold ${o.thresholdPct}%) \xB7 ${o.inheritedPct}% inferred from the previous ask`);for(let a of o.problems||[])t.push(`WARNING: ${a}`)}let s=r.uncategorized;if(s){t.push(""),t.push(`UNCATEGORIZED RESIDUE \xB7 ${Tc(s.usd)} (${s.sharePct}%) \xB7 ${s.turns} turn(s) \xB7 by shape: `+(Object.entries(s.byShape||{}).filter(([,a])=>a>0).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Tc(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${zo(Tc(a.usd),8)} ${zo(a.shape,10)} ${zo(String(a.track||"").slice(0,26),26)} | ${a.text}`);s.top?.length&&(t.push(""),t.push("novel-ask rows are vocabulary the classifier lacks: add a rule in agent/src/task-classifier.js (and a line in test/uncategorized-corpus.test.mjs). follow-up rows had no prior ask on their box."))}return t.join(`
|
|
692
|
+
`)}Fn();var Xr=36e5,E_=24*Xr,c2=400,l2=12,Jo=e=>Math.round(e*1e4)/1e4;function bg({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 Yo?String(e||"7d"):"7d",i=Yo[a];return{key:a,since:i==null?null:r-i,until:r}}var u2=(e,t)=>e!=null&&t-e<=48*Xr?Xr:E_;function S_(e,t){if(t===Xr)return Math.floor(e/Xr)*Xr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function d2(e,t){if(t===Xr)return e+Xr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var T_=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),A_=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},f2=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Jo(n)]));async function __(e,t={}){let n=t.now??Date.now(),r=bg({...t,now:n}),o=r.since==null?E_:u2(r.since,r.until),s=A=>(r.since==null||A>=r.since)&&A<r.until,a=r.since!=null?r.until-r.since:null,i=A=>a!=null&&A>=r.since-a&&A<r.since,c={},l={},u=T_(),f=0,d=0,p=0,h=0,w=0,g=0,b=new Set,v=new Map,x=[],I=[],P=[],B=null;for(let A of e||[]){let q=(await W(A)).filter(R=>R.type==="cost.recorded");if(!q.length){A.archivedAt||P.push(A.name||A.id);continue}let z=[];for(let R of q){let G=R.ts??0;s(G)?z.push(R):i(G)&&(g+=Ut(R).usd)}if(!z.length)continue;let F=Ar(z,{boxLabels:vc(A)});f+=F.totalUsd,d+=F.turns,p+=F.repricedTurns||0,h+=F.asRecordedUsd||0,w+=F.unpricedTurns||0;for(let R of F.unpricedModels||[])b.add(R);A_(u,F.totalTokens);for(let[R,G]of Object.entries(F.byModel))c[R]||={usd:0,events:0,tokens:T_()},c[R].usd=Jo(c[R].usd+G.usd),c[R].events+=G.events,A_(c[R].tokens,G.tokens);for(let[R,G]of Object.entries(F.byProvider))l[R]||={usd:0,events:0},l[R].usd=Jo(l[R].usd+G.usd),l[R].events+=G.events;for(let R of z){let{usd:G}=Ut(R);if(!G)continue;let X=S_(R.ts??0,o),K=v.get(X)??{t:X,total:0,byProvider:{}};K.total+=G;let he=R.provider||"unknown";K.byProvider[he]=(K.byProvider[he]||0)+G,v.set(X,K),(B==null||X<B)&&(B=X)}x.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:F.totalUsd,turns:F.turns,providers:Object.keys(F.byProvider)});for(let[R,G]of Object.entries(F.byBox))I.push({trackId:A.id,trackName:A.name||A.id,boxId:R,...G})}let j=r.since!=null?S_(r.since,o):B,L=[];for(let A=j,E=0;A!=null&&A<r.until&&E<c2;A=d2(A,o),E++){let q=v.get(A);L.push(q?{t:A,total:Jo(q.total),byProvider:f2(q.byProvider)}:{t:A,total:0,byProvider:{}})}return x.sort((A,E)=>E.usd-A.usd),I.sort((A,E)=>E.usd-A.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:As(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:L,fleet:{totalUsd:Jo(f),asRecordedUsd:Jo(h),turns:d,repricedTurns:p,unpricedTurns:w,unpricedModels:[...b],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:P},prior:a!=null?{totalUsd:Jo(g),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:I.slice(0,l2)}}bu();var p2=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),m2=20,h2=40;async function I_(e,t={}){let n=t.now??Date.now(),r=bg({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await W(l),f=0,d=vc(l);for(let p of u){if(!p2.has(p.type))continue;let h=p.type==="code.rework"?p.payload?.ts??p.ts:p.ts;p.type!=="rework.cause.answered"&&!o(h??0)||(p.type==="code.committed"&&f++,s.push(p.trackId?p:{...p,trackId:l.id}))}f&&i++;for(let p of l.boxes||[])a[`${l.id}/${p.name}`]={trackName:l.name||l.id,label:d[p.name]||p.name}}let c=nS(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:Br,decayEndDays:$m,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,intents:c.intents,noCodeDelta:c.noCodeDelta,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,m2),fixes:c.fixes.slice(0,h2)}}import w2 from"node:fs";import $_ from"node:fs/promises";import Ac from"node:os";import g2 from"node:path";wh();ut();var y2=1024*1024,Ad=e=>Math.round(e/y2);async function b2(e){try{let t=await $_.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Ad(r),diskTotalMb:Ad(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function k2(){try{return(await Zt()).filter(t=>t.isBostrat).length}catch{return null}}async function x2(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await $_.readdir(g2.join(uc,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function R_({stateDir:e=uc,now:t=Date.now}={}){let n=Ac.totalmem(),[r,o,s,a]=await Promise.all([Bu().catch(()=>null),b2(w2.existsSync(e)?e:Ac.tmpdir()),k2(),x2()]),i=Ac.cpus()?.length||null,c=Ac.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?Ad(n-r):null,memTotalMb:Ad(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Ac.uptime())}}ge();cp();Pa();Fn();ge();Fn();var v2=864e5;function S2(e,{tolerance:t=.005}={}){if(e.type!=="cost.recorded"||e.source==="vendor")return null;let n=e.payload?.tokens,r=Number(e.payload?.usd)||0;if(!(n?(n.input||0)+(n.output||0)+(n.cacheRead||0)+(n.cacheWrite??n.cacheCreation??0):0)||!r)return null;let s=e.payload?.model||"unknown",a=e.payload?.speed||void 0,i={input:n.input||0,output:n.output||0,cacheRead:n.cacheRead||0,cacheWrite:n.cacheWrite??n.cacheCreation??0},c=yn(s,i,e.ts,a?{speed:a}:void 0);if(!c)return null;let l=r-c,u=Math.abs(l)>Math.max(c*t,.001);return{model:s,speed:a??null,ts:e.ts,asRecorded:r,expected:c,delta:l,divergent:u}}async function O_(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*v2,s={},a=0,i=0,c=[];for(let u of e||[]){let f=await W(u);for(let d of f){if(!d.ts||d.ts<o)continue;let p=S2(d,{tolerance:n});if(!p)continue;a++;let h=p.speed?`${p.model} (${p.speed})`:p.model,w=s[h]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};w.rows++,w.asRecordedUsd+=p.asRecorded,w.expectedUsd+=p.expected,p.divergent&&(i++,w.divergent++,c.length<10&&c.push({track:u.name||u.id,...p}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}eh();Vl();ct();bn();import ha from"node:fs/promises";import T2 from"node:fs";import pt from"node:path";import A2 from"node:os";import{execFile as E2}from"node:child_process";import{promisify as _2}from"node:util";var I2=_2(E2),C_=process.env.BOSTRAT_STATE_DIR||pt.join(process.env.HOME||A2.tmpdir(),".bostrat"),Ed=!1,kg=null;async function xg(e,t){let n=await ha.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ha.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ha.mkdir(pt.dirname(t),{recursive:!0}),await ha.copyFile(e,t),!0)}var $2=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 _d(e,t,{skip:n=null}={}){let r=0,o=await ha.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if($2.has(s.name)||n?.has(s.name))continue;let a=pt.join(e,s.name);s.isDirectory()?r+=await _d(a,pt.join(t,s.name)):s.isFile()&&(r+=await xg(a,pt.join(t,s.name))?1:0)}return r}async function M_({stateDir:e=C_,dest:t=process.env.BOSTRAT_SYNC_DIR||`${C_}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ha.mkdir(t,{recursive:!0});let o=0;o+=await xg(pt.join(e,"tracks.json"),pt.join(t,"tracks.json"))?1:0,o+=await xg(pt.join(e,"events.jsonl"),pt.join(t,"events.jsonl"))?1:0,o+=await _d(pt.join(e,"events"),pt.join(t,"events")),o+=await _d(pt.join(e,"memory"),pt.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await _d(pt.join(u.cwd,".bostrat"),pt.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>I2("git",["-C",t,...u]);T2.existsSync(pt.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 uf(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||Ed))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Ed&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Ed=!1,kg=null}catch(u){Ed=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
693
|
+
`).slice(-3).join(" | ").slice(0,300);f!==kg&&(kg=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)}}}Ph();tn();lo();ut();Ia();import{execFile as R2}from"node:child_process";import{promisify as O2}from"node:util";import{randomUUID as C2}from"node:crypto";import M2 from"node:fs";import N2 from"node:os";import P2 from"node:path";var Qr=O2(R2),Id=e=>new Promise(t=>setTimeout(t,e)),eo=Cf,vg=process.env.BOSTRAT_USAGE_PROBE_CWD||P2.join(N2.homedir(),".bostrat","usage-probe"),D2=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,B2=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,j2=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,N_=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Zr=null,Ec=null,_c=null;async function L2(){try{return await Qr(J,Z("has-session","-t",eo)),!0}catch{return!1}}function F2(){Zr&&clearTimeout(Zr),Zr=setTimeout(()=>U2().catch(()=>{}),D2),Zr.unref&&Zr.unref()}async function U2(){Zr&&(clearTimeout(Zr),Zr=null);try{await Qr(J,Z("kill-session","-t",eo))}catch{}}async function H2(){let e=Date.now()+j2;for(;;){let t=await Sg().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Id(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Id(500)}}async function W2(){if(await L2())return;if(!await wr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");M2.mkdirSync(vg,{recursive:!0}),await cr(vg);let e=C2();await Qr(J,Z("new-session","-d","-s",eo,"-c",vg,"-x","200","-y","60",...qa()));let t=`${Of} --session-id ${e}`;await Qr(J,Z("send-keys","-t",eo,t,"Enter")),await H2()}async function Sg(){let{stdout:e}=await Qr(J,Z("capture-pane","-t",eo,"-p"));return e}async function q2(){await W2(),await Qr(J,Z("send-keys","-t",eo,"-l","/usage")),await Qr(J,Z("send-keys","-t",eo,"Enter"));let e=Date.now()+B2,t="";for(;await Id(400),t=await Sg(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Id(900),t=await Sg(),await Qr(J,Z("send-keys","-t",eo,"Escape")).catch(()=>{}),F2(),t}function G2(e){let t=String(e||"").split(`
|
|
694
|
+
`).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 K2(e){let t=String(e||"").split(`
|
|
695
|
+
`).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 P_({fresh:e=!1}={}){return!e&&Ec&&Date.now()-Ec.at<N_?Ec.data:_c||(_c=(async()=>{try{let t=await q2(),n=G2(t),r={...n,contributors:K2(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ec={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 Ec={at:Date.now()-(N_-3e3),data:n},n}finally{_c=null}})(),_c)}var V2=262144;function z2(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 Y2=new Set([300]),J2=new Set([10080]);function X2(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:Y2.has(t)?"session":J2.has(t)?"week":"other"}function Z2(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 Q2(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?z2((o-n)/1e3):null,resetsAt:o}}var Ic=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function eH(e,t=Date.now()){let n=String(e||"").split(`
|
|
696
|
+
`),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 Ic(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Ic(r);[c.primary,c.secondary].forEach((p,h)=>{let w=Q2(p,l,t);if(!w)return;let g=X2(p.window_minutes);g===null&&(g=h===0?"session":"week"),g==="session"&&!u.session?u.session=w:g==="week"&&!u.week?u.week=w:(u.other??=[]).push({label:Z2(p.window_minutes),...w})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let d=c.credits;return d&&(d.has_credits||d.unlimited)&&(u.credits={balance:String(d.balance??""),unlimited:!!d.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...Ic(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Ic(r)}return Ic(r)}async function Tg(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await io(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 At(n.file,V2),o=eH(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 D_(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 $d({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 Tg(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Tg(null))}return n.has("cursor")&&r.push(await $d({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function $d(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?Tg(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await P_({fresh:t})}}ur();ct();ct();function tH(e=process.env){return!!(Oe(e)&&e.BOSTRAT_PLANE_TOKEN)}var nH=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Rd=(e,t)=>Object.assign(new Error(e),{status:t});async function B_(e,t){if(!tH(t))throw Rd("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Oe(t)}${e}`,{headers:nH(t)})}catch(o){throw Rd(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Rd(r?.error||`plane replied ${n.status}`,n.status);return r}async function j_(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await B_(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function L_(e,{env:t=process.env}={}){if(!e)throw Rd("message id required",400);return(await B_(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}xi();iu();function Ag(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 rH(e){let[t,n]=e.split("/"),r=Ag(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 F_(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(rH).filter(Boolean)}function oH(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function U_(e){let t=F_(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:oH,allowed(r){if(n)return!0;let o=Ag((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function H_(e,t){let n=F_(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Ag(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import W_ from"node:crypto";import Od from"node:fs";import sH from"node:path";function q_(e=process.env,{stateDir:t=uc,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=sH.join(t,"api-token");try{let a=Od.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=W_.randomBytes(32).toString("base64url");try{Od.mkdirSync(t,{recursive:!0}),Od.writeFileSync(o,s+`
|
|
697
|
+
`,{mode:384}),Od.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function $c(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 G_(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:W_.timingSafeEqual(n,r)}function K_(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 aH=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],iH=e=>aH.some(t=>t.test(e));function V_({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=K_(o);return G_(c,e)?a():iH(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||$c(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 z_(e,t,{enforce:n=!0}={}){return!n||G_(K_(e),t)?!0:$c(e)}hn();Mu();Mu();import cH from"node:http";import lH from"node:net";function Cd(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 Md(e){let t=Cd(e.headers.host);return t?{slug:t,view:fh(t)}:null}var uH=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),dH=e=>{let t=uH(e.name);return`<!doctype html><meta charset="utf-8">
|
|
683
698
|
<meta http-equiv="refresh" content="3">
|
|
684
699
|
<title>${t} \u2014 starting</title>
|
|
685
700
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
686
701
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
687
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
702
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function Y_(e,t,n){let r=Md(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(dH(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=cH.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 J_(e,t,n){let r=Md(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
|
|
688
703
|
\r
|
|
689
704
|
bostrat: no live view "${s}"\r
|
|
690
|
-
`),t.destroy(),!0;let a=
|
|
705
|
+
`),t.destroy(),!0;let a=lH.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
|
|
691
706
|
`)+`\r
|
|
692
707
|
\r
|
|
693
|
-
`),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 h_(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}.${a2(n.host)}/`}function a2(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 yd(t)&&Um(n[0])?n.slice(1).join("."):t}Ks();eh();Iu();pm();me();import w_ from"node:os";import{randomUUID as ng}from"node:crypto";var i2=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||w_.hostname(),nodeName:e.BOSTRAT_NODE_NAME||w_.hostname()});var g_=32768,y_=120,b_=1e3,k_=new Set(["claude","codex","cursor"]),c2=new Set(["off","transition","nag"]),l2=[[0,59],[0,23],[1,31],[1,12],[0,6]];function u2(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=l2[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 an=e=>String(e??"").trim(),Go={name(e){let t=an(e);if(!t)throw new Error("a loop needs a name");if(t.length>y_)throw new Error(`name is too long (${t.length}); cap is ${y_}`);return t},schedule(e){let t=an(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!u2(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=an(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>g_)throw new Error(`prompt is too long (${t.length} chars); cap is ${g_}`);return t},expectation(e){let t=an(e)||null;if(t&&t.length>b_)throw new Error(`expectation is too long (${t.length}); cap is ${b_}`);return t},notify(e){let t=an(e)||"off";if(!c2.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=an(typeof t=="string"?t:t?.provider)||"claude";if(!k_.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...k_].join(", ")}`);let r=typeof t=="object"&&t&&an(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function d2(e,t){let n=e&&typeof e=="object"?e:{},r=Go.name(n.name),o=Go.schedule(n.schedule),s=Go.prompt(n.prompt),a=Go.expectation(n.expectation),i=Go.notify(n.notify),c=Go.assignment(n.assignment),l=an(t?.workspace?.repo)||an(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:f}=i2();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:f||null}}async function v_(e,t){let n=d2(t,e),r=ng();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 S_(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await F({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)}}var tg=["name","schedule","prompt","expectation","notify","assignment","enabled"],f2=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function T_(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function E_(e,t){let n=an(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function p2(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!n)throw new Error("a patch must be a JSON object of the fields to change");let r=Object.keys(n).filter(a=>!tg.includes(a));if(r.length){let a=r.filter(i=>["repo","nodeId","cwd","userId","owner","id","trackId"].includes(i));throw a.length?new Error(`a proposal can't change ${a.join(", ")} \u2014 retargeting a loop is a Loops-tab edit (scope D5)`):new Error(`unknown field(s) ${r.join(", ")} \u2014 patchable: ${tg.join(", ")}`)}let o={};for(let a of tg){if(!(a in n))continue;let i=Go[a](n[a]);(a==="assignment"?f2(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function A_(e,t,n){let r=T_(t),{patch:o,changed:s}=p2(n,r),a=ng(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await $({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var x_=600;async function __(e,t,n){let r=an(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>x_)throw new Error(`reason is too long (${r.length}); cap is ${x_}`);let o=T_(t),s=ng(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await $({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var I_=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function $_(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await I_(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await $(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await $(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function R_(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await I_(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await $(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await $(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}uu();me();ur();me();Fn();Iu();var O_=10,C_=200,m2=120,h2=24,N_=/^\d{4}-\d{2}-\d{2}$/;function rg(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var w2=(e=Date.now())=>new Date(e).toISOString().slice(0,10),pt=(e,t="i")=>new RegExp(e,t),g2=[{re:pt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:pt("\\bSPC\\b",""),reason:"business (SPC)"},{re:pt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:pt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:pt("\\bReplicas\\b",""),reason:"competitor research"},{re:pt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:pt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:pt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],y2=[pt("\\bSPC\\b",""),pt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),pt("competitor|cloudcli|\\bkiro\\b"),pt("\\bReplicas\\b",""),pt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),pt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),pt("(^|[^\\w&])#\\d+\\b"),pt("\\btrack/[\\w.-]+")];function b2({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of g2)if(o.test(n)||o.test(r))return s;return null}function M_(e){let t=String(e||"");for(let n of y2){let r=n.exec(t);if(r)return r[0].trim()}return null}function P_(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 k2=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function x2(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=ji(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(`
|
|
694
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,m2)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${h2}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=k2.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=M_(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>C_)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${C_}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=M_(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>O_)throw new Error(`${r.length} bullets \u2014 over the ${O_}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function v2(e,{repos:t=rg()}={}){let n=new Set(t);return(await xo()).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:b2({title:r.title,headRef:r.headRef})}))}async function D_(e,t){let n=await v2(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function B_(e,{md:t,day:n=null}){let r=x2(t),o=n==null||n===""?w2():String(n);if(!N_.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 j_(e,{day:t}){let n=String(t||"");if(!N_.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}}Bs();Fn();Eo();me();Bs();it();Nr();Fn();gi();eu();Si();Bn();import{execFile as S2}from"node:child_process";import{promisify as T2}from"node:util";var sg=T2(S2),L_=3e4,E2=30*6e4,A2=L_,_2=10*6e4,I2=10080*6e4,$2=60*6e4,R2=8,O2=1440*6e4,C2=3,F_="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",og=60*6e4,M2=15*6e4,N2=9e4,P2=1440*6e4,kd=new Map,xd=new Map,D2=/could not resolve|not found|no pull requests?/i,B2=e=>D2.test(String(e?.message||e||""));function j2(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<og)}var U_={fast:A2,slow:_2,decay:$2};function H_(e,t){return e.armedAt&&t-e.armedAt<E2||j2(e,t)||W_(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>I2?"decay":"slow"}function L2(e,t){return U_[H_(e,t)]}function Tc(e,t,n,r){let o=e.lanes?.get(oe(t.repo,t.number))||{};return Ql({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function F2(e,t){Hn("watch");try{let{stdout:n}=await sg("gh",["pr","view",String(t),"--repo",e,"--json",F_],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await ln({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Rr(n)&&Or(n),n}}async function U2(e){Hn("watch");try{let{stdout:t}=await sg("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",F_],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await ln({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Rr(t)&&Or(t),t}}async function H2(){let e=new Map;for(let t of await le().catch(()=>[]))e.set(t.id,t);return e}async function W2(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 q2(e,t){let n=await So(e);return n?Ps(n,t):null}async function K2(e,t,n){let r=await So(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ds(r,t,{method:n})}async function G2(e,t){let n=await So(e);if(!n)throw new Error(`no local checkout for ${e}`);return ki(n,t)}async function V2(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 da(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 z2=10*6e4;async function Y2(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>O2){await da(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 da(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await da(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<z2)return;await da(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 da(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 da(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await q_(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function fa(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 J2(e,t){try{return await sg("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Sc(e,t,n,r=()=>!0){return(await F(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var pa=(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 W_(e,t,{grace:n=N2}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=P2}async function X2(e,t,n,r){let o=r.now??Date.now();if(!W_(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ve(s))return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??To)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await F(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??Ln)(e.repo,e.number).catch(()=>null),!1):await(r.claim??Ln)(e.repo,e.number).catch(()=>null)?(await fa(s,"opened",{...pa(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 Z2(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 Kl(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Vl(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<M2;await Cx(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Ox(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Sc(a,"pushed",e.number,f=>f.payload?.sha===s)&&await fa(a,"pushed",{...pa(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>og)await Gl(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 Gl(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Tc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Sc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await fa(a,"ci-fixed",{...pa(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Gl(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>og)await zl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await zl(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Tc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Sc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await fa(a,"reconciled",{...pa(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await zl(e.repo,e.number).catch(()=>{})}}}}async function Q2(e,t,n,r){let o=Tc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Sc(s,"closed",e.number)&&await fa(s,"closed",{...pa(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 eH(e,t,n){Tc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Sc(r,"lost",e.number)&&await fa(r,"lost",pa(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function q_(e,t,n,r,o={}){let s=Tc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await F(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.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function tH(e={}){let{now:t=Date.now(),auth:n=Zp,view:r=F2,list:o=U2,links:s=Dx,trackMap:a=H2,emitMerged:i=W2,mark:c=Ir,archiveIfArmed:l=qn,notifyIfArmed:u=Kn,notifyIfResolved:f=Lu,followupsIfMerged:d=Bo,batchThreshold:p=C2,detail:h=q2,merge:w=K2,updateBranch:g=G2,lookup:k=tt,disarm:S=wi,emitDisarmed:E=V2,localHasCommit:R=J2,claim:B=Ln,midTurn:T=To}=e;if(!(await n().catch(()=>null))?.authenticated||Xl()>0)return 0;let O=await s().catch(()=>[]),I=av(O),U=O.filter(W=>t-(kd.get(oe(W.repo,W.number))||0)>=L2(W,t));if(!U.length)return 0;let H=Date.now(),ae=new Map,X=new Map;for(let W of[...U].sort(xi)){let be=H_(W,t);ae.set(oe(W.repo,W.number),{lane:be,intervalMs:U_[be]}),kd.set(oe(W.repo,W.number),t),X.has(W.repo)||X.set(W.repo,[]),X.get(W.repo).push(W)}let C=await a().catch(()=>new Map),Q={now:t,lanes:ae,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,followupsIfMerged:d,detail:h,merge:w,updateBranch:g,lookup:k,disarm:S,emitDisarmed:E,localHasCommit:R,claim:B,midTurn:T};for(let[W,be]of X){let se,pe=new Set;if(be.length>p)se=await o(W).catch(()=>null);else{se=new Map;for(let ie of be)try{let L=await r(W,ie.number);L?.number!=null&&se.set(Number(L.number),L)}catch(L){B2(L)&&pe.add(oe(ie.repo,ie.number))}}if(se)for(let ie of be){let L=oe(ie.repo,ie.number),ce=se.get(Number(ie.number));if(!ce){if(pe.has(L)){let ue=(xd.get(L)||0)+1;xd.set(L,ue),ue>=R2&&(await eH(ie,C,Q),xd.delete(L),kd.delete(L))}continue}if(xd.delete(L),ce.state==="MERGED"||ce.state==="CLOSED")ce.state==="MERGED"?await q_(ie,ce,C,Q):await Q2(ie,ce,C,Q),kd.delete(L);else{await X2(ie,ce,C,Q).catch(()=>{}),await Z2(ie,ce,C,Q).catch(()=>{});let ue=!ie.trackId||I.get(ie.trackId)===L;ie.autoMerge&&ue&&await Y2(ie,C,Q).catch(()=>{})}}}let ve={};for(let{lane:W}of ae.values())ve[W]=(ve[W]||0)+1;return console.log(`[pr-watch] probed ${U.length} link(s) (${Object.entries(ve).map(([W,be])=>`${W} ${be}`).join(", ")}) in ${Date.now()-H}ms`),U.length}function K_(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await tH()}catch{}finally{e=!1}}},L_)}me();Fn();ar();Nr();Bn();function G_(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function nH(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var rH=new Set(["merged","closed","lost"]);function oH(e,t){if(!e?.blocked)return null;let n=G_(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 sH(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function aH(e,t,n,r,o){return(await F(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 iH(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 V_(e,t={}){let{now:n=Date.now(),slugOf:r=Se,lookup:o=tt,note:s=Mx,tracks:a=async()=>(await le()).filter(f=>!Ve(f)),asked:i=aH,emitChange:c=iH}=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(!nH(p))continue;let h=await o(d,p.number).catch(()=>null);if(!h?.trackId||rH.has(h.state))continue;let{prev:w}=await s(d,p.number,{blocked:G_(p),sha:p.headSha??null,at:n});if(!w)continue;let g=oH(w,p);if(!g||sH(h,g.change))continue;u??=new Map((await a()).map(R=>[R.id,R]));let k=u.get(h.trackId);if(!k)continue;let S=p.headSha??null;if(await i(k,g.change,p.number,S,w.at||0))continue;let E={number:Number(p.number),url:p.url||h.url,title:p.title||h.title,branch:p.headRefName||h.headRef,repo:d,sha:S,delegated:!1,reason:g.reason};g.change==="ci-fixed"?E.passed=p.checks.passed:E.base=p.baseRefName||null,await c(k,g.change,E),l.push({change:g.change,number:p.number,reason:g.reason})}catch{}}return l}lt();me();Nr();Bn();import{randomUUID as cH}from"node:crypto";var lH=2e4,z_=3,ma=new Map,ag=new Set;async function Y_(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 uH(e={}){let{sessions:t=null,tracks:n=null,append:r=$,read:o=F,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await zt(),i=n??await le()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ve(u)||!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=(ma.get(f.name)||0)+1;ma.set(f.name,p),p===z_&&(ag.add(f.name),await Y_(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:cH(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(ma.get(f.name)||0)>=z_;if(ma.delete(f.name),p||!ag.has(f.name)){ag.add(f.name);let h=await Y_(u,f.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ma.keys()])l.has(u)||ma.delete(u)}var Ec=null,ig=!1;function J_(){return Ec?()=>{}:(Ec=setInterval(()=>{ig||(ig=!0,uH().catch(()=>{}).finally(()=>{ig=!1}))},lH),Ec.unref?.(),()=>{clearInterval(Ec),Ec=null})}bo();qm();me();Bs();Eo();bo();import{randomUUID as dH}from"node:crypto";async function fH(){let e=await Promise.resolve().then(()=>(Nr(),vE));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var cg={listRepoPrs:hn,readEvents:F,appendEvent:$,sayDeps:fH,currentBranch:Cr};async function pH(e,t=cg,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Cr)(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 lg(e,t,{actions:n,notify:r=!1}={},o=cg){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 pH(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=dH();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 ug(e,{offerId:t,choice:n}={},r=cg){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=Ar.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Os(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,xx(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 rr from"node:fs/promises";import ha from"node:path";import rI from"node:os";import X_ from"node:fs/promises";import vd from"node:path";import{execFile as mH}from"node:child_process";import{promisify as hH}from"node:util";import{fileURLToPath as wH}from"node:url";var gH=hH(mH),Q_=vd.dirname(wH(import.meta.url)),yH=vd.resolve(Q_,"..","..","client","dist"),bH="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",kH=new Map([...bH].map((e,t)=>[e,t]));function xH(e){let t=[],n=0,r=0;for(let o of e){let s=kH.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 vH(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=xH(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 SH(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 Z_=new Map;async function TH(e){let t;try{t=await X_.stat(e)}catch{return null}let n=Z_.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await X_.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:vH(r.mappings||"")};return Z_.set(e,o),o}catch{return null}}var dg=null;function EH(){return dg||(dg=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):gH("git",["rev-parse","--short","HEAD"],{cwd:vd.resolve(Q_,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),dg}var AH=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function _H(e,{distDir:t=yH}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(AH)){let[a,i,c,l]=s,u=await TH(vd.join(t,"assets",`${i}.map`)),f=u?SH(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 eI(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await EH();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await _H(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var IH=()=>process.env.BOSTRAT_STATE_DIR||ha.join(process.env.HOME||rI.tmpdir(),".bostrat"),Qr=()=>ha.join(IH(),"telemetry"),oI=()=>ha.join(Qr(),".relay-offsets.json"),$H=50,RH=14,OH=3e4,tI=500,jt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function CH(e,t){if(!e||typeof e!="object")return null;let n=jt(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:jt(e.anonId,64),sessionId:jt(e.sessionId,64),userId:jt(e.userId,64),release:jt(e.release,64),appVersion:jt(e.appVersion,32),route:jt(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:jt(e.error.kind,256),message:jt(e.error.message,8192),stack:jt(e.error.stack,32768),fingerprint:jt(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:jt(s?.n,64),r:jt(s?.r,128)}))),o}var sI=e=>new Date(e).toISOString().slice(0,10),nI=0;async function aI(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,$H):[]).map(s=>CH(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await eI(s);await rr.mkdir(Qr(),{recursive:!0});let o=ha.join(Qr(),`${sI(t)}.ndjson`);return await rr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
695
|
-
`).join(""),"utf8"),t-
|
|
696
|
-
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=tI){let p=f.slice(d,d+tI).map(w=>NH(w)),h=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(!h||!h.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await lI(r)}return{sent:o,disabled:!1}}var fg=null,pg=!1;function uI(){fg||(fg=setInterval(async()=>{if(!pg){pg=!0;try{await PH()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{pg=!1}}},OH),fg.unref?.())}import Ed from"node:fs/promises";import hI from"node:path";import DH from"node:os";var wI=()=>process.env.BOSTRAT_STATE_DIR||hI.join(process.env.HOME||DH.tmpdir(),".bostrat"),gI=()=>hI.join(wI(),"dictation-glossary.json"),BH=100,jH=500,LH=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Ad=null,mg=null,hg=Promise.resolve();function dI(){let e={},t=Date.now();for(let n of LH)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function yI(){return Ad||(mg??=Ed.readFile(gI(),"utf8").then(e=>JSON.parse(e)?.terms??dI()).catch(()=>dI()).then(e=>Ad=e).finally(()=>mg=null),mg)}async function fI(){let e=JSON.stringify({terms:Ad},null,2),t=gI(),n=`${t}.${process.pid}.tmp`;await Ed.mkdir(wI(),{recursive:!0}),await Ed.writeFile(n,e),await Ed.rename(n,t)}function FH(){return hg=hg.then(fI,fI),hg.catch(()=>{})}async function wg(){return Object.values(await yI()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var UH=e=>Math.max(1,Math.ceil(e.length/4));async function HH(){let e=[],t=0;for(let{term:n}of await wg()){let r=UH(n);if(!(t+r>jH)&&(e.push(n),t+=r,e.length>=BH))break}return e}async function WH(e,t){let n=await yI(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var pI=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Td=e=>e.toLowerCase();function mI(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function bI(e,t){let n=pI(e),r=pI(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]=Td(n[u])===Td(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;)Td(n[c])===Td(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(mI(r[l])&&i.add(r[l]),l++);for(;l<s;l++)mI(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await WH(u,"learned");return await FH(),[...i]}var Ac=null;function qH(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 kI(){if(!Ac)return null;let e=await uc();if(!e||e.apiKey.slice(-6)!==Ac.keyTail)return null;let{keyTail:t,...n}=Ac;return n}async function xI(e,t){let n=await uc();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 HH())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=qH(o.status,i);c.persistent&&(Ac={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 Ac=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}me();var gg={"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 KH(e){if(!e)return null;let t=String(e).trim();if(gg[t])return t;let n=null;for(let r of Object.keys(gg))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function GH(e,t={}){let n=gg[KH(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 VH(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 _c=null;async function zH(){if(!_c)return null;let e=await rd();if(!e||e.apiKey.slice(-6)!==_c.keyTail)return null;let{keyTail:t,...n}=_c;return n}async function YH(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=VH(t.status,o);s.persistent&&(_c={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}_c=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 JH={openai:YH};async function vI(){let e=await rd();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await zH()}:{configured:!1}}async function SI(){let e=await rd();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 JH[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function TI(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=GH(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}}Bn();var OI=xe.dirname(ZH(import.meta.url)),EI=Number(process.env.PORT)||3362,Id=fw(process.env),CI=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,wa=s_(process.env.BOSTRAT_ALLOW_IPS),vg=a_(process.env.BOSTRAT_VIEW_FRONT_CIDRS,bd),MI=c_(),NI=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",v=Gt();v.disable("x-powered-by");v.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),yd(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});v.use((e,t,n)=>{if(vc(e)||wa.allowed(wa.clientIp(e))||vg.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});v.use(p_);v.use(d_({token:MI,enforce:NI}));v.use("/api/mocks",Gt.json({limit:"1mb"}));v.use("/api/mocks",Gt.raw({type:"image/png",limit:"24mb"}));v.use("/api/mocks",Gt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));v.use("/api/runbooks",Gt.json({limit:"1mb"}));v.use("/api/tracks/adopt",Gt.json({limit:"1mb"}));v.use("/api/telemetry",Gt.json({limit:"256kb"}));v.use("/api/hook",Gt.json({limit:"2mb"}));v.use("/api/dictation",Gt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));v.use("/api/tracks/:id/upload",Gt.raw({type:()=>!0,limit:"25mb"}));v.use(Gt.json({limit:"64kb"}));v.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:$t,hostTmuxAlive:await fr(),autoTriggerPct:CI,plane:ga?ga.status():null})});v.get("/api/sessions",async(e,t)=>{t.json({sessions:await zt()})});v.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 pr(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})}});v.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Pe(n))return t.status(400).json({error:"invalid session name"});if(!await ge(n))return t.status(404).json({error:"session not found"});try{await Yt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});v.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Pe(n))return t.status(400).json({error:"invalid session name"});let r=await ge(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Ka(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});v.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Pe(n))return t.status(400).json({error:"invalid session name"});try{let r=await Bk(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)})}});v.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=Il(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 AI=10;v.get("/api/idle-guard",async(e,t)=>{try{t.json(dw({tracks:await le(),views:await ku(),queuedOf:n=>Mo(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Kt=Lo(),rW=DE({kind:Kt}),Ic=null,kt=()=>Ic&&["auto","prompt","off"].includes(Ic)?Ic:rW,xg=null,oW=cc.has(Kt),PI=async({to:e,from:t})=>!oW||Ju(await lc())?!1:($c.arm({to:e,from:t,kind:Kt}),!0);async function sW(e){let t=kt();Ic=e??null;let n=kt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){$c.cancel();return}xg&&await PI(xg)}}var DI=(()=>{try{return nW(import.meta.url)("../package.json").version||null}catch{return null}})(),$c=jE({isBusy:async()=>dw({tracks:await le(),views:await ku(),queuedOf:e=>Mo(e)}).busy,onStuck:({to:e,from:t})=>An({current:t,latest:e,installKind:Kt,policy:kt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?An({current:t,latest:e,installKind:Kt,policy:kt(),status:"updating",reason:"auto"}):An({current:t,latest:e,installKind:Kt,policy:kt(),status:"failed",error:n?.reason})).catch(()=>{})});v.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Kt,policy:kt(),policySource:Ic?"plane":"node",current:DI,status:await lc(),armed:$c.armed})});v.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 Xu({to:n,kind:Kt,reason:"manual"});if(r.ok)return $c.cancel(),An({current:r.from,latest:n,installKind:Kt,policy:kt(),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 BI(){try{let e=await BE();e&&await An({current:e.from,latest:e.to,installKind:e.kind||Kt,policy:kt(),status:e.status,error:e.error,reason:e.reason}),await Rh({current:DI,installKind:Kt,policy:kt()})}catch{}}BI();setInterval(BI,3e4).unref();v.get("/api/tracks",async(e,t)=>{let n=(await le()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>AI){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,AI).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Xi,agentAvailability:await Mu(),tickets:await cA()})});v.get("/api/cost",async(e,t)=>{try{let n=await le();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Vw(n);if(r.cursorUsage=Tm(),e.query.format==="text")return t.type("text/plain").send(BA(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/roundup",async(e,t)=>{try{t.json(await FA(await le(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/spend",async(e,t)=>{try{let n=await KA(await le(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Tm(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=fi(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/caps",(e,t)=>{t.json(fi())});v.post("/api/usage/caps",async(e,t)=>{try{t.json(await Ll(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});v.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await e_(await le(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Dg(e.get(Rn),{byToken:Qi,legacy:()=>Ii(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Sa("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Sa("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&&Sa("mismatch",{track:r.track.id,box:r.box.name}),Sa(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 RA(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&zc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await Qi(e.get(Rn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await CA(n.track,n.box)).join(`
|
|
708
|
+
`),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 X_(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}.${fH(n.host)}/`}function fH(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 Cd(t)&&fh(n[0])?n.slice(1).join("."):t}Xs();Th();Hu();Hm();ge();import Z_ from"node:os";import{randomUUID as _g}from"node:crypto";var pH=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||Z_.hostname(),nodeName:e.BOSTRAT_NODE_NAME||Z_.hostname()});var Q_=32768,eI=120,tI=1e3,nI=new Set(["claude","codex","cursor"]),mH=new Set(["off","transition","nag"]),hH=[[0,59],[0,23],[1,31],[1,12],[0,6]];function wH(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=hH[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(),Xo={name(e){let t=dn(e);if(!t)throw new Error("a loop needs a name");if(t.length>eI)throw new Error(`name is too long (${t.length}); cap is ${eI}`);return t},schedule(e){let t=dn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!wH(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=dn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>Q_)throw new Error(`prompt is too long (${t.length} chars); cap is ${Q_}`);return t},expectation(e){let t=dn(e)||null;if(t&&t.length>tI)throw new Error(`expectation is too long (${t.length}); cap is ${tI}`);return t},notify(e){let t=dn(e)||"off";if(!mH.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=dn(typeof t=="string"?t:t?.provider)||"claude";if(!nI.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...nI].join(", ")}`);let r=typeof t=="object"&&t&&dn(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function gH(e,t){let n=e&&typeof e=="object"?e:{},r=Xo.name(n.name),o=Xo.schedule(n.schedule),s=Xo.prompt(n.prompt),a=Xo.expectation(n.expectation),i=Xo.notify(n.notify),c=Xo.assignment(n.assignment),l=dn(t?.workspace?.repo)||dn(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:f}=pH();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:f||null}}async function oI(e,t){let n=gH(t,e),r=_g();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 sI(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await W({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)}}var Eg=["name","schedule","prompt","expectation","notify","assignment","enabled"],yH=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function aI(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function iI(e,t){let n=dn(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function bH(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!n)throw new Error("a patch must be a JSON object of the fields to change");let r=Object.keys(n).filter(a=>!Eg.includes(a));if(r.length){let a=r.filter(i=>["repo","nodeId","cwd","userId","owner","id","trackId"].includes(i));throw a.length?new Error(`a proposal can't change ${a.join(", ")} \u2014 retargeting a loop is a Loops-tab edit (scope D5)`):new Error(`unknown field(s) ${r.join(", ")} \u2014 patchable: ${Eg.join(", ")}`)}let o={};for(let a of Eg){if(!(a in n))continue;let i=Xo[a](n[a]);(a==="assignment"?yH(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function cI(e,t,n){let r=aI(t),{patch:o,changed:s}=bH(n,r),a=_g(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await $({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var rI=600;async function lI(e,t,n){let r=dn(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>rI)throw new Error(`reason is too long (${r.length}); cap is ${rI}`);let o=aI(t),s=_g(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await $({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var uI=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await W({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function dI(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await uI(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await $(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await $(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function fI(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await uI(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await $(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await $(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}Au();ge();mr();ge();Wn();Hu();var pI=10,mI=200,kH=120,xH=24,wI=/^\d{4}-\d{2}-\d{2}$/;function Ig(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var vH=(e=Date.now())=>new Date(e).toISOString().slice(0,10),mt=(e,t="i")=>new RegExp(e,t),SH=[{re:mt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:mt("\\bSPC\\b",""),reason:"business (SPC)"},{re:mt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:mt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:mt("\\bReplicas\\b",""),reason:"competitor research"},{re:mt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:mt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:mt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],TH=[mt("\\bSPC\\b",""),mt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),mt("competitor|cloudcli|\\bkiro\\b"),mt("\\bReplicas\\b",""),mt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),mt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),mt("(^|[^\\w&])#\\d+\\b"),mt("\\btrack/[\\w.-]+")];function AH({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of SH)if(o.test(n)||o.test(r))return s;return null}function hI(e){let t=String(e||"");for(let n of TH){let r=n.exec(t);if(r)return r[0].trim()}return null}function gI(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 EH=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function _H(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Wi(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(`
|
|
709
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,kH)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${xH}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=EH.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=hI(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>mI)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${mI}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=hI(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>pI)throw new Error(`${r.length} bullets \u2014 over the ${pI}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function IH(e,{repos:t=Ig()}={}){let n=new Set(t);return(await So()).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:AH({title:r.title,headRef:r.headRef})}))}async function yI(e,t){let n=await IH(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function bI(e,{md:t,day:n=null}){let r=_H(t),o=n==null||n===""?vH():String(n);if(!wI.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 kI(e,{day:t}){let n=String(t||"");if(!wI.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}}Eo();Wn();Io();ge();Eo();ct();Yn();Wn();xi();xu();iu();_i();bn();import{execFile as FH}from"node:child_process";import{promisify as UH}from"node:util";var $H=new Set(["pull_request","check_suite","workflow_run","push"]),RH=/^[^/\s]+\/[^/\s]+$/,OH=500,$g=new Set,Rg=[],Nd=new Map,zt={hourStart:0,byEvent:{},duplicates:0,rejected:0};function Og(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==zt.hourStart&&(zt.hourStart=r,zt.byEvent={},zt.duplicates=0,zt.rejected=0),t==="accepted"?zt.byEvent[n]=(zt.byEvent[n]||0)+1:zt[t]+=1}function CH(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!$H.has(e.event)||typeof e.repo!="string"||!RH.test(e.repo))return null;let t=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(r=>Number.isInteger(r)&&r>0),n=Number.isInteger(e.number)&&e.number>0?e.number:t[0]??null;return{forge:"github",deliveryId:typeof e.deliveryId=="string"&&e.deliveryId?e.deliveryId.slice(0,128):null,event:e.event,action:typeof e.action=="string"?e.action.slice(0,64):null,repo:e.repo,number:n,numbers:n&&!t.includes(n)?[n,...t]:t,headSha:typeof e.headSha=="string"?e.headSha.slice(0,64):null,headRef:typeof e.headRef=="string"?e.headRef.slice(0,256):null,baseRef:typeof e.baseRef=="string"?e.baseRef.slice(0,256):null,merged:typeof e.merged=="boolean"?e.merged:null,draft:typeof e.draft=="boolean"?e.draft:null,conclusion:typeof e.conclusion=="string"?e.conclusion.slice(0,64):null,deleted:e.deleted===!0,senderLogin:typeof e.senderLogin=="string"?e.senderLogin.slice(0,128):null,ts:Number.isFinite(e.ts)?e.ts:null}}function xI(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=CH(e);if(!o)return Og(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if($g.has(o.deliveryId))return Og(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for($g.add(o.deliveryId),Rg.push(o.deliveryId);Rg.length>OH;)$g.delete(Rg.shift())}let s=o.repo.toLowerCase(),a=Nd.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Nd.set(s,a),Og(t,"accepted",o.event),r.log?.(`[github-event] ${o.event}${o.action?` ${o.action}`:""} ${o.repo}${o.number?` #${o.number}`:o.headRef?` ${o.headRef}`:""}${o.conclusion?` (${o.conclusion})`:""}${o.deliveryId?` delivery ${o.deliveryId.slice(0,8)}`:""}`);try{n?.(o)}catch(i){r.error?.("[github-event] onAccept failed:",i?.message||i)}return{ok:!0,duplicate:!1,envelope:o}}function MH(e,t=Date.now()){let n=Nd.get(String(e||"").toLowerCase());return n?{covered:!0,lastDeliveryAt:n.lastDeliveryAt,ageMs:t-n.lastDeliveryAt,count:n.count}:{covered:!1,lastDeliveryAt:null,ageMs:null,count:0}}var vI=1440*6e4;function Cg(e,t=Date.now()){let n=MH(e,t);return n.covered&&n.ageMs<vI}function SI(e=Date.now()){let t={};for(let[n,r]of Nd)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<vI};return{hourStart:zt.hourStart,byEvent:{...zt.byEvent},duplicates:zt.duplicates,rejected:zt.rejected,repos:t}}ge();Wn();ur();Yn();bn();function TI(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function NH(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var PH=new Set(["merged","closed","lost"]);function DH(e,t){if(!e?.blocked)return null;let n=TI(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 BH(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function jH(e,t,n,r,o){return(await W(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 LH(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 Pd(e,t={}){let{now:n=Date.now(),slugOf:r=Ee,lookup:o=ot,note:s=dv,tracks:a=async()=>(await fe()).filter(f=>!Ge(f)),asked:i=jH,emitChange:c=LH}=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(!NH(p))continue;let h=await o(d,p.number).catch(()=>null);if(!h?.trackId||PH.has(h.state))continue;let{prev:w}=await s(d,p.number,{blocked:TI(p),sha:p.headSha??null,at:n});if(!w)continue;let g=DH(w,p);if(!g||BH(h,g.change))continue;u??=new Map((await a()).map(I=>[I.id,I]));let b=u.get(h.trackId);if(!b)continue;let v=p.headSha??null;if(await i(b,g.change,p.number,v,w.at||0))continue;let x={number:Number(p.number),url:p.url||h.url,title:p.title||h.title,branch:p.headRefName||h.headRef,repo:d,sha:v,delegated:!1,reason:g.reason};g.change==="ci-fixed"?x.passed=p.checks.passed:x.base=p.baseRefName||null,await c(b,g.change,x),l.push({change:g.change,number:p.number,reason:g.reason})}catch{}}return l}var Pg=UH(FH),OI=3e4,HH=30*6e4,WH=OI,qH=10*6e4,GH=10080*6e4,KH=60*6e4,VH=8,CI=1440*6e4,zH=3,MI="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Ng=60*6e4,YH=15*6e4,JH=9e4,XH=1440*6e4,Dd=new Map,Bd=new Map,ZH=/could not resolve|not found|no pull requests?/i,QH=e=>ZH.test(String(e?.message||e||""));function eW(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Ng)}var AI={fast:WH,slow:qH,decay:KH};function EI(e,t,n=Cg){return NI(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<HH||eW(e,t))?"fast":t-(e.firstSeen||0)>GH?"decay":"slow"}function Oc(e,t,n,r){let o=e.lanes?.get(ie(t.repo,t.number))||{};return au({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function tW(e,t){Gn("watch");try{let{stdout:n}=await Pg("gh",["pr","view",String(t),"--repo",e,"--json",MI],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await pn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Nr(n)&&Pr(n),n}}async function nW(e){Gn("watch");try{let{stdout:t}=await Pg("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",MI],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await pn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Nr(t)&&Pr(t),t}}async function rW(){let e=new Map;for(let t of await fe().catch(()=>[]))e.set(t.id,t);return e}async function oW(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}}),await ku(e,t).catch(()=>{})}async function sW(e,t){let n=await Ao(e);return n?Us(n,t):null}async function aW(e,t,n){let r=await Ao(e);if(!r)throw new Error(`no local checkout for ${e}`);return Hs(r,t,{method:n})}async function iW(e,t){let n=await Ao(e);if(!n)throw new Error(`no local checkout for ${e}`);return Ti(n,t)}async function cW(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 wa(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 lW=10*6e4;async function uW(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>CI){await wa(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 wa(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await wa(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<lW)return;await wa(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 wa(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 wa(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await PI(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function ga(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 dW(e,t){try{return await Pg("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Rc(e,t,n,r=()=>!0){return(await W(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var ya=(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 NI(e,t,{grace:n=JH}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=XH}async function fW(e,t,n,r){let o=r.now??Date.now();if(!NI(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ge(s))return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??_o)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await W(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 ga(s,"opened",{...ya(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 pW(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 Zl(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await eu(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<YH;await uv(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await lv(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Rc(a,"pushed",e.number,f=>f.payload?.sha===s)&&await ga(a,"pushed",{...ya(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Ng)await Ql(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 Ql(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Oc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Rc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await ga(a,"ci-fixed",{...ya(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Ql(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Ng)await tu(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await tu(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Oc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Rc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await ga(a,"reconciled",{...ya(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await tu(e.repo,e.number).catch(()=>{})}}}}async function mW(e,t,n,r){let o=Oc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Rc(s,"closed",e.number)&&await ga(s,"closed",{...ya(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 hW(e,t,n){Oc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Rc(r,"lost",e.number)&&await ga(r,"lost",ya(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function PI(e,t,n,r,o={}){let s=Oc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await W(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.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function _I(e={}){let{now:t=Date.now(),auth:n=gm,view:r=tW,list:o=nW,links:s=mv,trackMap:a=rW,emitMerged:i=oW,mark:c=Cr,archiveIfArmed:l=Vn,notifyIfArmed:u=zn,notifyIfResolved:f=Qu,followupsIfMerged:d=Fo,batchThreshold:p=zH,detail:h=sW,merge:w=aW,updateBranch:g=iW,lookup:b=ot,disarm:v=ki,emitDisarmed:x=cW,localHasCommit:I=dW,claim:P=Hn,midTurn:B=_o,only:j=null,wantDetail:L=null,covered:A=Cg,verdicts:E=null}=e;if(!(await n().catch(()=>null))?.authenticated||ou()>0)return 0;let z=await s().catch(()=>[]),F=jv(z),R=Y=>{try{return!!L?.(Y)}catch{return!1}},G=j?z.filter(Y=>{try{return j(Y)}catch{return!1}}):z.filter(Y=>t-(Dd.get(ie(Y.repo,Y.number))||0)>=AI[EI(Y,t,A)]);if(!G.length)return 0;let X=Date.now(),K=new Map,he=new Map;for(let Y of[...G].sort(Ai)){let Re=j?"event":EI(Y,t,A);K.set(ie(Y.repo,Y.number),{lane:Re,intervalMs:AI[Re]??null}),Dd.set(ie(Y.repo,Y.number),t),he.has(Y.repo)||he.set(Y.repo,[]),he.get(Y.repo).push(Y)}let ce=await a().catch(()=>new Map),re=new Map,Ae=(Y,Re)=>{let pe=ie(Y,Re);if(!re.has(pe)){let Ie=Promise.resolve().then(()=>h(Y,Re));Ie.catch(()=>{}),re.set(pe,Ie)}return re.get(pe)},U=E??((Y,Re)=>Pd([{path:Y,prs:[Re]}],{now:t,slugOf:async()=>Y,tracks:async()=>[...ce.values()].filter(pe=>!Ge(pe))}).catch(()=>{})),de={now:t,lanes:K,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,followupsIfMerged:d,detail:Ae,merge:w,updateBranch:g,lookup:b,disarm:v,emitDisarmed:x,localHasCommit:I,claim:P,midTurn:B};for(let[Y,Re]of he){let pe,Ie=new Set,_=N=>N.ciFixAwait||N.reconcileAwait||R(N)?!0:!N.autoMerge||N.trackId&&F.get(N.trackId)!==ie(N.repo,N.number)?!1:t-(N.autoMerge.armedAt||0)<=CI;if(Re.length>p)pe=await o(Y).catch(()=>null);else{pe=new Map;for(let N of Re){if(_(N)){let xe=await Ae(Y,N.number).catch(()=>null);if(xe?.state){pe.set(Number(N.number),{number:Number(N.number),url:xe.url,title:xe.title,headRefName:xe.headRefName,headRefOid:xe.headSha??null,state:xe.state,mergedAt:xe.mergedAt??null,closedAt:xe.closedAt??null});continue}}try{let xe=await r(Y,N.number);xe?.number!=null&&pe.set(Number(xe.number),xe)}catch(xe){QH(xe)&&Ie.add(ie(N.repo,N.number))}}}if(pe)for(let N of Re){let xe=ie(N.repo,N.number),Ne=pe.get(Number(N.number));if(!Ne){if(Ie.has(xe)){let Xe=(Bd.get(xe)||0)+1;Bd.set(xe,Xe),Xe>=VH&&(await hW(N,ce,de),Bd.delete(xe),Dd.delete(xe))}continue}if(Bd.delete(xe),Ne.state==="MERGED"||Ne.state==="CLOSED")Ne.state==="MERGED"?await PI(N,Ne,ce,de):await mW(N,Ne,ce,de),Dd.delete(xe);else{await fW(N,Ne,ce,de).catch(()=>{}),await pW(N,Ne,ce,de).catch(()=>{});let Xe=!N.trackId||F.get(N.trackId)===xe;N.autoMerge&&Xe&&await uW(N,ce,de).catch(()=>{});try{if(R(N)&&!re.has(xe)&&Ae(N.repo,N.number),re.has(xe)){let wt=await re.get(xe).catch(()=>null);wt?.checks&&await U(N.repo,wt)}}catch{}}}}let ae={};for(let{lane:Y}of K.values())ae[Y]=(ae[Y]||0)+1;return console.log(`[pr-watch] probed ${G.length} link(s) (${Object.entries(ae).map(([Y,Re])=>`${Y} ${Re}`).join(", ")}) in ${Date.now()-X}ms`),G.length}function II(e,t){return!e||!t?!1:e.all?!0:String(t.repo||"").toLowerCase()!==e.repo?!1:e.numbers?.length?e.numbers.includes(Number(t.number)):e.event==="push"?!!e.branch&&t.headRef===e.branch||!!t.autoMerge:e.branch?t.headRef===e.branch:!0}var Mg=!1,jd=!1,Ld=[],$I=Promise.resolve();async function wW(){for(;jd||Ld.length;){if(jd){jd=!1;try{await _I()}catch{}continue}let e=Ld;Ld=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await _I({...n,only:r=>t.some(o=>II(o,r)),wantDetail:r=>t.some(o=>o.detail&&II(o,r))})}catch{}}}function Fd(e,t){return e===null?jd=!0:Ld.push({spec:e,deps:t}),Mg||(Mg=!0,$I=wW().finally(()=>{Mg=!1})),$I}function DI(e,t=void 0){let n=String(e?.repo||"").toLowerCase();if(!/^[^/\s]+\/[^/\s]+$/.test(n))return Promise.resolve();let r=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(o=>Number.isInteger(o)&&o>0);return Fd({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var gW=6e4,RI=0;function BI(e=void 0){let t=e?.now??Date.now();return t-RI<gW?Promise.resolve():(RI=t,Fd({all:!0},e))}function jI(){Fd(null),setInterval(()=>Fd(null),OI)}Eo();Dm();Du();Yn();bn();var yW=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function bW({tracks:e=fe,reconcile:t=Pu}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ge(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function LI(e,t={}){let{invalidate:n=Wt,probe:r=DI,poke:o=bS,reconcileOpened:s=bW}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&yW.has(e.action)){if(e.action==="opened"||e.action==="reopened"){a.poked=!0,a.reconciled=!0;try{o()}catch{}s()}a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event}}else(e.event==="check_suite"||e.event==="workflow_run")&&e.action==="completed"?a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event,detail:!0}:e.event==="push"&&!e.deleted&&(a.probe={repo:e.repo,branch:e.headRef||null,event:"push"});return a.probe&&r(a.probe),a}ut();ge();Yn();bn();import{randomUUID as kW}from"node:crypto";var xW=2e4,FI=3,ba=new Map,Dg=new Set;async function UI(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 vW(e={}){let{sessions:t=null,tracks:n=null,append:r=$,read:o=W,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Zt(),i=n??await fe()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ge(u)||!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=(ba.get(f.name)||0)+1;ba.set(f.name,p),p===FI&&(Dg.add(f.name),await UI(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:kW(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(ba.get(f.name)||0)>=FI;if(ba.delete(f.name),p||!Dg.has(f.name)){Dg.add(f.name);let h=await UI(u,f.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ba.keys()])l.has(u)||ba.delete(u)}var Cc=null,Bg=!1;function HI(){return Cc?()=>{}:(Cc=setInterval(()=>{Bg||(Bg=!0,vW().catch(()=>{}).finally(()=>{Bg=!1}))},xW),Cc.unref?.(),()=>{clearInterval(Cc),Cc=null})}xo();Du();ge();Eo();Io();xo();import{randomUUID as SW}from"node:crypto";async function TW(){let e=await Promise.resolve().then(()=>(Yn(),aE));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var jg={listRepoPrs:kn,readEvents:W,appendEvent:$,sayDeps:TW,currentBranch:Dr};async function AW(e,t=jg,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Dr)(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 Lg(e,t,{actions:n,notify:r=!1}={},o=jg){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 AW(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=SW();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 Fg(e,{offerId:t,choice:n}={},r=jg){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=Rr.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Bs(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,Qx(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 ir from"node:fs/promises";import ka from"node:path";import YI from"node:os";import WI from"node:fs/promises";import Ud from"node:path";import{execFile as EW}from"node:child_process";import{promisify as _W}from"node:util";import{fileURLToPath as IW}from"node:url";var $W=_W(EW),GI=Ud.dirname(IW(import.meta.url)),RW=Ud.resolve(GI,"..","..","client","dist"),OW="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",CW=new Map([...OW].map((e,t)=>[e,t]));function MW(e){let t=[],n=0,r=0;for(let o of e){let s=CW.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 NW(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=MW(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 PW(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 qI=new Map;async function DW(e){let t;try{t=await WI.stat(e)}catch{return null}let n=qI.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await WI.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:NW(r.mappings||"")};return qI.set(e,o),o}catch{return null}}var Ug=null;function BW(){return Ug||(Ug=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):$W("git",["rev-parse","--short","HEAD"],{cwd:Ud.resolve(GI,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Ug}var jW=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function LW(e,{distDir:t=RW}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(jW)){let[a,i,c,l]=s,u=await DW(Ud.join(t,"assets",`${i}.map`)),f=u?PW(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 KI(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await BW();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await LW(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var FW=()=>process.env.BOSTRAT_STATE_DIR||ka.join(process.env.HOME||YI.tmpdir(),".bostrat"),to=()=>ka.join(FW(),"telemetry"),JI=()=>ka.join(to(),".relay-offsets.json"),UW=50,HW=14,WW=3e4,VI=500,Bt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function qW(e,t){if(!e||typeof e!="object")return null;let n=Bt(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:Bt(e.anonId,64),sessionId:Bt(e.sessionId,64),userId:Bt(e.userId,64),release:Bt(e.release,64),appVersion:Bt(e.appVersion,32),route:Bt(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:Bt(e.error.kind,256),message:Bt(e.error.message,8192),stack:Bt(e.error.stack,32768),fingerprint:Bt(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Bt(s?.n,64),r:Bt(s?.r,128)}))),o}var XI=e=>new Date(e).toISOString().slice(0,10),zI=0;async function ZI(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,UW):[]).map(s=>qW(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await KI(s);await ir.mkdir(to(),{recursive:!0});let o=ka.join(to(),`${XI(t)}.ndjson`);return await ir.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
710
|
+
`).join(""),"utf8"),t-zI>6*3600*1e3&&(zI=t,GW(t).catch(()=>{})),r.length}async function GW(e=Date.now()){let t=XI(e-HW*24*3600*1e3),n=[];try{n=await ir.readdir(to())}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 ir.rm(ka.join(to(),s),{force:!0});let o=await e$();for(let s of r)delete o[s];await t$(o)}var Hd=new Map;function QI(e,t=Date.now()){let n=e||"unknown",r=Hd.get(n);return r||(Hd.size>512&&Hd.clear(),r={tokens:40,at:t},Hd.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 e$(){try{return JSON.parse(await ir.readFile(JI(),"utf8"))||{}}catch{return{}}}async function t$(e){await ir.mkdir(to(),{recursive:!0}),await ir.writeFile(JI(),JSON.stringify(e),"utf8")}function KW(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||YI.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 VW({fetchImpl:e=fetch}={}){let t=await CE();if(!t)return{sent:0,disabled:!0};let n;try{n=(await ir.readdir(to())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await e$(),o=0;for(let s of n){let a=ka.join(to(),s),i=r[s]||0,c;try{let d=await ir.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(`
|
|
711
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=VI){let p=f.slice(d,d+VI).map(w=>KW(w)),h=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(!h||!h.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await t$(r)}return{sent:o,disabled:!1}}var Hg=null,Wg=!1;function n$(){Hg||(Hg=setInterval(async()=>{if(!Wg){Wg=!0;try{await VW()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Wg=!1}}},WW),Hg.unref?.())}import qd from"node:fs/promises";import i$ from"node:path";import zW from"node:os";var c$=()=>process.env.BOSTRAT_STATE_DIR||i$.join(process.env.HOME||zW.tmpdir(),".bostrat"),l$=()=>i$.join(c$(),"dictation-glossary.json"),YW=100,JW=500,XW=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Gd=null,qg=null,Gg=Promise.resolve();function r$(){let e={},t=Date.now();for(let n of XW)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function u$(){return Gd||(qg??=qd.readFile(l$(),"utf8").then(e=>JSON.parse(e)?.terms??r$()).catch(()=>r$()).then(e=>Gd=e).finally(()=>qg=null),qg)}async function o$(){let e=JSON.stringify({terms:Gd},null,2),t=l$(),n=`${t}.${process.pid}.tmp`;await qd.mkdir(c$(),{recursive:!0}),await qd.writeFile(n,e),await qd.rename(n,t)}function ZW(){return Gg=Gg.then(o$,o$),Gg.catch(()=>{})}async function Kg(){return Object.values(await u$()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var QW=e=>Math.max(1,Math.ceil(e.length/4));async function eq(){let e=[],t=0;for(let{term:n}of await Kg()){let r=QW(n);if(!(t+r>JW)&&(e.push(n),t+=r,e.length>=YW))break}return e}async function tq(e,t){let n=await u$(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var s$=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Wd=e=>e.toLowerCase();function a$(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function d$(e,t){let n=s$(e),r=s$(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]=Wd(n[u])===Wd(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;)Wd(n[c])===Wd(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(a$(r[l])&&i.add(r[l]),l++);for(;l<s;l++)a$(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await tq(u,"learned");return await ZW(),[...i]}var Mc=null;function nq(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 f$(){if(!Mc)return null;let e=await hc();if(!e||e.apiKey.slice(-6)!==Mc.keyTail)return null;let{keyTail:t,...n}=Mc;return n}async function p$(e,t){let n=await hc();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 eq())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=nq(o.status,i);c.persistent&&(Mc={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 Mc=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ge();var Vg={"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 rq(e){if(!e)return null;let t=String(e).trim();if(Vg[t])return t;let n=null;for(let r of Object.keys(Vg))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function oq(e,t={}){let n=Vg[rq(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 sq(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 Nc=null;async function aq(){if(!Nc)return null;let e=await gd();if(!e||e.apiKey.slice(-6)!==Nc.keyTail)return null;let{keyTail:t,...n}=Nc;return n}async function iq(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=sq(t.status,o);s.persistent&&(Nc={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}Nc=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 cq={openai:iq};async function m$(){let e=await gd();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await aq()}:{configured:!1}}async function h$(){let e=await gd();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 cq[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function w$(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=oq(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}}bn();var S$=Te.dirname(uq(import.meta.url)),g$=Number(process.env.PORT)||3362,Vd=Dw(process.env),T$=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,xa=U_(process.env.BOSTRAT_ALLOW_IPS),Zg=H_(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Md),A$=q_(),E$=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",S=jt();S.disable("x-powered-by");S.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Cd(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($c(e)||xa.allowed(xa.clientIp(e))||Zg.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(Y_);S.use(V_({token:A$,enforce:E$}));S.use("/api/mocks",jt.json({limit:"1mb"}));S.use("/api/mocks",jt.raw({type:"image/png",limit:"24mb"}));S.use("/api/mocks",jt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));S.use("/api/runbooks",jt.json({limit:"1mb"}));S.use("/api/tracks/adopt",jt.json({limit:"1mb"}));S.use("/api/telemetry",jt.json({limit:"256kb"}));S.use("/api/hook",jt.json({limit:"2mb"}));S.use("/api/dictation",jt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",jt.raw({type:()=>!0,limit:"25mb"}));S.use(jt.json({limit:"64kb"}));S.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:It,hostTmuxAlive:await wr(),autoTriggerPct:T$,plane:va?va.status():null})});S.get("/api/sessions",async(e,t)=>{t.json({sessions:await Zt()})});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 gr(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(!Le(n))return t.status(400).json({error:"invalid session name"});if(!await ke(n))return t.status(404).json({error:"session not found"});try{await Qt(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(!Le(n))return t.status(400).json({error:"invalid session name"});let r=await ke(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Ya(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(!Le(n))return t.status(400).json({error:"invalid session name"});try{let r=await h0(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=Pl(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 y$=10;S.get("/api/idle-guard",async(e,t)=>{try{t.json(Pw({tracks:await fe(),views:await Cu(),queuedOf:n=>Do(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Yt=Ho(),hq=kE({kind:Yt}),Pc=null,xt=()=>Pc&&["auto","prompt","off"].includes(Pc)?Pc:hq,Xg=null,wq=pc.has(Yt),_$=async({to:e,from:t})=>!wq||ud(await mc())?!1:(Dc.arm({to:e,from:t,kind:Yt}),!0);async function gq(e){let t=xt();Pc=e??null;let n=xt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Dc.cancel();return}Xg&&await _$(Xg)}}var I$=(()=>{try{return mq(import.meta.url)("../package.json").version||null}catch{return null}})(),Dc=vE({isBusy:async()=>Pw({tracks:await fe(),views:await Cu(),queuedOf:e=>Do(e)}).busy,onStuck:({to:e,from:t})=>Rn({current:t,latest:e,installKind:Yt,policy:xt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Rn({current:t,latest:e,installKind:Yt,policy:xt(),status:"updating",reason:"auto"}):Rn({current:t,latest:e,installKind:Yt,policy:xt(),status:"failed",error:n?.reason})).catch(()=>{})});S.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Yt,policy:xt(),policySource:Pc?"plane":"node",current:I$,status:await mc(),armed:Dc.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 dd({to:n,kind:Yt,reason:"manual"});if(r.ok)return Dc.cancel(),Rn({current:r.from,latest:n,installKind:Yt,policy:xt(),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 $$(){try{let e=await xE();e&&await Rn({current:e.from,latest:e.to,installKind:e.kind||Yt,policy:xt(),status:e.status,error:e.error,reason:e.reason}),await ew({current:I$,installKind:Yt,policy:xt()})}catch{}}$$();setInterval($$,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await fe()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>y$){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,y$).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:nc,agentAvailability:await Vu(),tickets:await KE()})});S.get("/api/cost",async(e,t)=>{try{let n=await fe();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await wg(n);if(r.cursorUsage=Qm(),e.query.format==="text")return t.type("text/plain").send(g_(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/roundup",async(e,t)=>{try{let n=await yg(await fe(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(v_(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/spend",async(e,t)=>{try{let n=await __(await fe(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Qm(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=wi(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(wi())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await Kl(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 D_(await fe(),{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 fy(e.get(Nn),{byToken:oc,legacy:()=>Ni(e.body?.session_id,e.body?.cwd)});if(n==="refused")return _a("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return _a("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&&_a("mismatch",{track:r.track.id,box:r.box.name}),_a(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 m_(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&tl(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await oc(e.get(Nn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await oS(n.track,n.box)).join(`
|
|
697
712
|
`)+`
|
|
698
|
-
`)}catch{t.status(204).end()}});v.post("/api/git/committed",async(e,t)=>{try{let n=await Qi(e.get(Rn));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?xe.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?xe.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+xe.sep))?s:o;MA(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await NA(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});v.get("/api/usage/rework",async(e,t)=>{try{t.json(await GA(await le(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/telemetry",async(e,t)=>{try{if(!iI(wa.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await aI(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/repos",async(e,t)=>{try{let n=await On(),r=new Set((await Promise.all(n.map(a=>Se(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await _a()).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)})}});v.get("/api/github/token",async(e,t)=>{try{t.json(await Lc(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/github/status",(e,t)=>{t.json({configured:jc(),calls:Gx(),latency:Jx()})});v.post("/api/github/refresh",(e,t)=>{Gg(),t.json({ok:!0})});v.get("/api/gmail/search",async(e,t)=>{try{let n=await n_(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)})}});v.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await r_(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Sg(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 $d({repo:e=null,number:t=null,branch:n=null}){let r=e?await Se(e):null;if(r&&t!=null){let s=await tt(r,t),a=s&&await te(s.trackId);if(a)return a}if(!n)return null;let o=await le();return au(o,n,s=>Sg(s,e,r))}async function jI({repo:e=null,number:t=null,branch:n=null}){let r=await $d({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ve(r))return r;if(!n)return null;let o=e?await Se(e).catch(()=>null):null,s=await le();return au(s,n,a=>!Ve(a)&&Sg(a,e,o))}async function cn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await $d({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Se(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 aW(e){let t=(await le()).filter(n=>!Ve(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Se(n.path);if(r)for(let o of n.prs){if(await tt(r,o.number))continue;let s=await au(t,o.headRefName,a=>Sg(a,n.path,r));s&&await Ot({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}v.get("/api/git/prs",async(e,t)=>{try{let n=await rv();await aW(n.repos).catch(()=>{}),await V_(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.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(!bi.has(w))throw new Error(`invalid merge method: ${w}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let k=await Se((await Wn(n)).path);if(!k)throw new Error("repo has no GitHub remote");let S=await $d({repo:n,number:g,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ki(n,g),await Ot({repo:k,number:g,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await mi(k,g,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await ko(k,g,i===!0),await hi(k,g,c===!0&&i!==!0),gn(),await cn("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:g,auto:!0})}let d=await Ds(n,r,{method:o||"squash"});await cn("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await Se(n).catch(()=>null),h=!1;if(p&&(i===!0&&await ko(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await hi(p,d.number,!0).catch(()=>{}),await Kn(p,d.number).catch(()=>{}),await Bo(p,d.number).catch(()=>{}),h=await qn(p,d.number).catch(()=>!1),await Ir(p,d.number,"merged").catch(()=>{})),i===!0&&!h){let w=await $d({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Do(w.id).catch(()=>{})}t.json(d)}catch(d){let p=d?.message||String(d),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(h).json({error:p})}});v.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 Se((await Wn(n)).path).catch(()=>null),l=c?await wi(c,i).catch(()=>null):null;try{await tm(n,i)}catch(u){if(!l)throw u}gn(),await cn("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})}});v.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Se(n).catch(()=>null),s=await cv(n,{method:r||"merge",onMerged:a=>{cn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Kn(o,a.number).catch(()=>{}).then(()=>Bo(o,a.number)).catch(()=>{}).then(()=>qn(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})}});v.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:iv()})});v.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(!bi.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 xo()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed").sort(xi);if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await ko(d.repo,d.number,o===!0).catch(()=>{}),await hi(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 h=await So(d.repo);if(!h)throw new Error(`no local checkout for ${d.repo}`);let w=await Ps(h,d.number);f.push({r:p,l:d,repoPath:h,pr:w,plan:ov(w,{update:r!==!1})})}catch(h){p.ok=!1,p.action="failed",p.reason=h?.message||String(h)}}for(let{r:d,l:p,repoPath:h,pr:w,plan:g}of sv(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await Ds(h,p.number,{method:i}),await cn("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await Kn(p.repo,p.number).catch(()=>{}),await Bo(p.repo,p.number).catch(()=>{}),await qn(p.repo,p.number).catch(()=>{}),await Ir(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await ki(h,p.number).catch(()=>{}),d.action="updated-armed"),!await mi(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await cn("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){d.ok=!1,d.action="failed",d.reason=k?.message||String(k)}if(gn(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await ko(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await hi(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})}});v.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 xo()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await wi(a.repo,a.number).catch(()=>null),c=await So(a.repo).catch(()=>null);c&&await tm(c,a.number).catch(()=>{}),i&&(await cn("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}))}gn(),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})}});v.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=Dt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let g=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!g)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return g});else if(f=await _o(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let g=await rm(w.dir,{title:r,body:o,base:s}),k=w.repo||await Se(w.dir).catch(()=>null);k&&g.number&&await Ot({repo:k,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await cn("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??w.name}}),p.push({repo:w.name,slug:k??null,ok:!0,url:g.url,number:g.number})}catch(g){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:g?.message||String(g)})}let h=p.filter(w=>w.ok);return h.length?t.json({ok:!0,multi:!0,results:p,url:h[0].url,number:h[0].number}):t.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await rm(i.cwd,{title:r,body:o,base:s}),l=await Se(i.cwd).catch(()=>null);l&&c.number&&await Ot({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await cn("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})}});v.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 _o(n),o=new Map(r.map(a=>[a.base,a])),s=Dt(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)})}});v.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 Uh(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})}});v.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Hh(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})}});v.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Ps(n,r),s=await Se(n).catch(()=>null),a=await jI({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=Dt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await lv(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?xe.join("ci",i.name):"ci",f=xe.join(a.cwd,".bostrat",u),d=xe.join(".bostrat",u,`pr-${o.number}-fail.log`);await mt.promises.mkdir(f,{recursive:!0}),await mt.promises.writeFile(xe.join(a.cwd,d),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
699
|
-
${
|
|
713
|
+
`)}catch{t.status(204).end()}});S.post("/api/git/committed",async(e,t)=>{try{let n=await oc(e.get(Nn));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?Te.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Te.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Te.sep))?s:o;sS(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await iS(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});S.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await cS(n,String(e.body?.sha||""))})}catch(n){let r=n?.message||String(n);t.status(/no standing answer/i.test(r)?409:400).json({error:r})}});S.get("/api/usage/rework",async(e,t)=>{try{t.json(await I_(await fe(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/telemetry",async(e,t)=>{try{if(!QI(xa.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await ZI(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 Pn(),r=new Set((await Promise.all(n.map(a=>Ee(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Oa()).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 Kc(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:Gc(),calls:Tv(),latency:Iv(),webhook:SI()})});S.post("/api/github/event",jt.json({limit:"64kb"}),(e,t)=>{let n=xI(e.body,{onAccept:r=>LI(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});S.post("/api/github/refresh",(e,t)=>{vy(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await j_(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 L_(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Qg(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 zd({repo:e=null,number:t=null,branch:n=null}){let r=e?await Ee(e):null;if(r&&t!=null){let s=await ot(r,t),a=s&&await oe(s.trackId);if(a)return a}if(!n)return null;let o=await fe();return pu(o,n,s=>Qg(s,e,r))}async function R$({repo:e=null,number:t=null,branch:n=null}){let r=await zd({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ge(r))return r;if(!n)return null;let o=e?await Ee(e).catch(()=>null):null,s=await fe();return pu(s,n,a=>!Ge(a)&&Qg(a,e,o))}async function fn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await zd({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Ee(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 yq(e){let t=(await fe()).filter(n=>!Ge(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Ee(n.path);if(r)for(let o of n.prs){if(await ot(r,o.number))continue;let s=await pu(t,o.headRefName,a=>Qg(a,n.path,r));s&&await Rt({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 Pv();await yq(n.repos).catch(()=>{}),await Pd(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 w=o||"merge";if(!Si.has(w))throw new Error(`invalid merge method: ${w}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let b=await Ee((await Kn(n)).path);if(!b)throw new Error("repo has no GitHub remote");let v=await zd({repo:n,number:g,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 Ti(n,g),await Rt({repo:b,number:g,trackId:v.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await yi(b,g,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await vo(b,g,i===!0),await bi(b,g,c===!0&&i!==!0),Wt(),await fn("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:v,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:g,auto:!0})}let d=await Hs(n,r,{method:o||"squash"});await fn("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await Ee(n).catch(()=>null),h=!1;if(p&&(i===!0&&await vo(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await bi(p,d.number,!0).catch(()=>{}),await zn(p,d.number).catch(()=>{}),await Fo(p,d.number).catch(()=>{}),h=await Vn(p,d.number).catch(()=>!1),await Cr(p,d.number,"merged").catch(()=>{})),i===!0&&!h){let w=await zd({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Lo(w.id).catch(()=>{})}t.json(d)}catch(d){let p=d?.message||String(d),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(h).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 Ee((await Kn(n)).path).catch(()=>null),l=c?await ki(c,i).catch(()=>null):null;try{await km(n,i)}catch(u){if(!l)throw u}Wt(),await fn("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 Ee(n).catch(()=>null),s=await Fv(n,{method:r||"merge",onMerged:a=>{fn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&zn(o,a.number).catch(()=>{}).then(()=>Fo(o,a.number)).catch(()=>{}).then(()=>Vn(o,a.number)).catch(()=>{}).then(()=>Cr(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:Lv()})});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(!Si.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await oe(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await So()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed").sort(Ai);if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await vo(d.repo,d.number,o===!0).catch(()=>{}),await bi(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 h=await Ao(d.repo);if(!h)throw new Error(`no local checkout for ${d.repo}`);let w=await Us(h,d.number);f.push({r:p,l:d,repoPath:h,pr:w,plan:Dv(w,{update:r!==!1})})}catch(h){p.ok=!1,p.action="failed",p.reason=h?.message||String(h)}}for(let{r:d,l:p,repoPath:h,pr:w,plan:g}of Bv(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await Hs(h,p.number,{method:i}),await fn("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await zn(p.repo,p.number).catch(()=>{}),await Fo(p.repo,p.number).catch(()=>{}),await Vn(p.repo,p.number).catch(()=>{}),await Cr(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await Ti(h,p.number).catch(()=>{}),d.action="updated-armed"),!await yi(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await fn("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(b){d.ok=!1,d.action="failed",d.reason=b?.message||String(b)}if(Wt(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await vo(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await bi(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 oe(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await So()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ki(a.repo,a.number).catch(()=>null),c=await Ao(a.repo).catch(()=>null);c&&await km(c,a.number).catch(()=>{}),i&&(await fn("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}))}Wt(),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 oe(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Pt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let g=u.find(b=>b.name===w||b.repo===w||b.base===w||b.dir===w);if(!g)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return g});else if(f=await Ro(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let g=await vm(w.dir,{title:r,body:o,base:s}),b=w.repo||await Ee(w.dir).catch(()=>null);b&&g.number&&await Rt({repo:b,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await fn("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??w.name}}),p.push({repo:w.name,slug:b??null,ok:!0,url:g.url,number:g.number})}catch(g){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:g?.message||String(g)})}let h=p.filter(w=>w.ok);return h.length?t.json({ok:!0,multi:!0,results:p,url:h[0].url,number:h[0].number}):t.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await vm(i.cwd,{title:r,body:o,base:s}),l=await Ee(i.cwd).catch(()=>null);l&&c.number&&await Rt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await fn("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 oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ro(n),o=new Map(r.map(a=>[a.base,a])),s=Pt(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 dw(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 fw(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 Us(n,r),s=await Ee(n).catch(()=>null),a=await R$({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=Pt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await Uv(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Te.join("ci",i.name):"ci",f=Te.join(a.cwd,".bostrat",u),d=Te.join(".bostrat",u,`pr-${o.number}-fail.log`);await ht.promises.mkdir(f,{recursive:!0}),await ht.promises.writeFile(Te.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
714
|
+
${b.log??"(no fetchable log \u2014 see link)"}
|
|
700
715
|
`).join(`
|
|
701
|
-
`));let p=l.map(k=>k.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await Pt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. 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 Ot({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Ix(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await cn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.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})}});v.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Ps(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Se(n).catch(()=>null),a=await jI({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=Dt(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 Pt(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 Ot({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await $x(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await cn("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})}});v.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=Ar.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 F(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 lg(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 Pt(a.id,Os(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&&Dh(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})}});v.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 ug(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 Rc=e=>t=>({...t,url:h_(t,e.headers)});v.get("/api/views",async(e,t)=>{try{let n=await ku({trackId:e.query.track||void 0});t.json({views:n.map(Rc(e)),maxLive:Br})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/views/:id",async(e,t)=>{let n=await $S(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Rc(e)(n),session:Hs(n.slug)})});v.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 le()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Hm({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:Rc(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})}});v.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Rc(e)(await RS(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Rc(e)(await OS(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})}});v.delete("/api/views/:id",async(e,t)=>{try{t.json(await CS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var iW=[["linear",Fo,Sw],["jira",nd,xw]],yg=new Map;v.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await kw(),r={};for(let[o,s,a]of iW){let i=await s();if(!i){yg.delete(o),r[o]={configured:!1};continue}let c=yg.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},yg.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)})}});v.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await kw()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await VE(o),i=n?await te(n):null;!i&&r&&(i=(await le()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+xe.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 Vo=async(e,t)=>{let n=e?await te(e):null;return!n&&t&&(n=(await le()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+xe.sep)))||null),n};v.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await hw()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});v.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Vo(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await oa(o);await Zu(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})}});v.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Vo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Zu(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var LI=async(e,t,n)=>{if(e)return{ref:e,track:await Vo(t,n)};let r=await Vo(t,n),o=r?await gw(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}};v.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 LI(o,n,r),l=await LE({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})}});v.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await LI(e.query.circuit,e.query.track,e.query.cwd);t.json(await ww(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})}});v.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Vo(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=await HE(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...f})}catch(u){let f=u?.message||String(u);t.status(/required/i.test(f)?400:502).json({error:f})}});v.get("/api/decisions",async(e,t)=>{try{let n=await Vo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await WE(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})}});v.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await Vo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await gw(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 FE(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})}});v.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 le()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+xe.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 ia(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})}});v.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 le()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+xe.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 lA(),ticket:await In(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});v.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await In(e.params.id)})});v.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 ia(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})}});v.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Pw(e.params.id)})});var Rd=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;v.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await pA(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Rd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Dw(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Rd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await mA(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Rd(r)).json({error:r})}});v.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 hA(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Rd(s)).json({error:s})}});v.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Zh(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})}});v.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 le()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+xe.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 gA(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})}});v.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await wA()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});v.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 yA(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})}});v.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Bi({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/mocks/:id",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Oc={dotfiles:"allow"};v.get("/api/mocks/:id/png",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=tn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Pi(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,Oc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});v.get("/api/mocks/:id/media",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=tn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Pi(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,Oc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});v.get("/api/mocks/:id/html",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=tn(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(Wt(n,r.v).html,Oc,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});v.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Tu(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 le()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+xe.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(h=>h.name===f.leadBox),p=await YS({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})}});v.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Tu(n)?n:null;try{let o=await JS(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})}});v.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await QS(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Pt(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})}});v.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await gT(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await eT(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})}});v.get("/api/flows",async(e,t)=>{try{t.json({flows:await fT({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/flows/:id",async(e,t)=>{let n=await pT(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});v.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 le()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.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 mT({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})}});v.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await hT(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})}});v.delete("/api/flows/:id",async(e,t)=>{try{t.json(await wT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await le(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+xe.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await ET({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)})}});v.get("/api/runbooks/:id",async(e,t)=>{let n=await nh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});v.get("/api/runbooks/:id/md",async(e,t)=>{let n=await nh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=ST(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 AT(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Qn(n,r.v),Oc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});v.get("/api/loops",async(e,t)=>{t.json(await Ti())});v.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 le()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+xe.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Ti();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 fm({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})});v.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 le()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+xe.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 v_(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var Tg=async(e,t)=>{let n=e?await te(e):null;return!n&&t&&(n=(await le()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+xe.sep)))||null),n};v.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Tg(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,result:await Uv(s,o,{ship:{changedRepos:_o}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});v.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Tg(n,r);if(!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 Wv(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});v.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await Tg(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:gm,...await zv(r)})});v.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted: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 Kv(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Pt,unarchiveTrack:Fu})})}catch(a){let i=a?.message||String(a),c=a?.code==="removed"?410:/unknown offer/i.test(i)?404:/already answered/i.test(i)?409:/required|not on this card/i.test(i)?400:/unreachable/i.test(i)?503:502;t.status(c).json({error:i})}});v.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Hv(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});v.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId: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 qv(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});ct.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||te(e.trackId).then(async s=>{if(s&&(n&&await Ai(s,n,o),t==="opened"||t==="pushed")){let a=Vv(await F(s).catch(()=>[]));a&&await Ai(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var FI=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await te(n):null;if(!s&&r&&(s=(await le()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Ti();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=E_(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};v.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await FI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await A_(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});v.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await FI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await __(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var UI=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;v.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt: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,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(UI(a)).json({error:a})}});v.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action: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 R_(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(UI(s)).json({error:s})}});v.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 S_(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})}});v.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 le()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.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 _T({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})}});v.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await IT(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})}});v.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await $T(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})}});v.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await RT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/changelog/draft",async(e,t)=>{try{let n=P_(e.query.since||null);t.json({since:n,repos:rg(),...await D_(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var HI=async({trackId:e,cwd:t})=>{let n=e?await te(e):null;return!n&&t&&(n=(await le()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+xe.sep)))||null),n};v.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await HI({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 B_(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})}});v.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await HI({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 j_(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var cW=120,bg=400,ga=null,_I=null;v.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>bg)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${bg} cap; a notification is a nudge, put the detail in chat`});let c=ji(`${n||""}
|
|
702
|
-
${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
|
|
716
|
+
`));let p=l.map(b=>b.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await Nt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. 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 Rt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await av(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await fn("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})}});S.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Us(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Ee(n).catch(()=>null),a=await R$({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=Pt(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 Nt(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 Rt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await iv(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await fn("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=Rr.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 oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await W(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 Lg(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 Nt(a.id,Bs(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&&aw(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 oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Fg(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 Bc=e=>t=>({...t,url:X_(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await Cu({trackId:e.query.track||void 0});t.json({views:n.map(Bc(e)),maxLive:Fr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await mT(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Bc(e)(n),session:zs(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 oe(n):null;if(!i&&s&&(i=(await fe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Te.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await ph({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:Bc(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:Bc(e)(await hT(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:Bc(e)(await wT(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 gT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var bq=[["linear",Wo,Vw],["jira",wd,Gw]],zg=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await qw(),r={};for(let[o,s,a]of bq){let i=await s();if(!i){zg.delete(o),r[o]={configured:!1};continue}let c=zg.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},zg.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 qw()})}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 OE(o),i=n?await oe(n):null;!i&&r&&(i=(await fe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Te.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 Zo=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await fe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Te.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Lw()})}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 Zo(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 ua(o);await fd(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 Zo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await fd(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var O$=async(e,t,n)=>{if(e)return{ref:e,track:await Zo(t,n)};let r=await Zo(t,n),o=r?await Uw(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 O$(o,n,r),l=await SE({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 O$(e.query.circuit,e.query.track,e.query.cwd);t.json(await Fw(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});S.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Zo(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=await EE(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...f})}catch(u){let f=u?.message||String(u);t.status(/required/i.test(f)?400:502).json({error:f})}});S.get("/api/decisions",async(e,t)=>{try{let n=await Zo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await _E(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 Zo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Uw(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 TE(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 oe(n):null;if(!s&&r&&(s=(await fe()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Te.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 pa(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 oe(r):null;if(!o&&n&&(o=(await fe()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Te.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 VE(),ticket:await Cn(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 Cn(e.params.id)})});S.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await pa(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 sg(e.params.id)})});var Yd=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 XE(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Yd(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await ag(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Yd(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await ZE(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Yd(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 oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await QE(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Yd(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 vw(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 oe(n):null;if(!s&&r&&(s=(await fe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Te.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){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 e_()})}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 oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await n_(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 Hi({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 Qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var jc={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await Qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=sn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Fi(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,jc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await Qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=sn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Fi(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,jc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await Qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=sn(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(Gt(n,r.v).html,jc,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&&ju(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 oe(a):null;if(!f&&c&&(f=(await fe()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+Te.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(h=>h.name===f.leadBox),p=await MT({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&&ju(n)?n:null;try{let o=await NT(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 BT(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Nt(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 tA(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await jT(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 JT({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 XT(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 oe(n):null;if(!a&&s&&(a=(await fe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Te.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 ZT({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 QT(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 eA(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 fe(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Te.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await lA({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 Eh(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 Eh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=iA(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 uA(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(rr(n,r.v),jc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await Ri())});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 fe()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Te.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Ri();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 Um({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 oe(n):null;if(!s&&r&&(s=(await fe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Te.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 oI(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var ey=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await fe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Te.sep)))||null),n};S.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await ey(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,result:await DS(s,o,{ship:{changedRepos:Ro}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});S.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await ey(n,r);if(!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 jS(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});S.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await ey(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:Km,...await WS(r)})});S.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await FS(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Nt,unarchiveTrack:ed})})}catch(a){let i=a?.message||String(a),c=a?.code==="removed"?410:/unknown offer/i.test(i)?404:/already answered/i.test(i)?409:/required|not on this card/i.test(i)?400:/unreachable/i.test(i)?503:502;t.status(c).json({error:i})}});S.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await BS(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});S.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await LS(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});lt.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||oe(e.trackId).then(async s=>{if(s&&(n&&await Ci(s,n,o),t==="opened"||t==="pushed")){let a=HS(await W(s).catch(()=>[]));a&&await Ci(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var C$=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await oe(n):null;if(!s&&r&&(s=(await fe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Te.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Ri();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=iI(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};S.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await C$(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await cI(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});S.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await C$(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await lI(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var M$=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;S.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await dI(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(M$(a)).json({error:a})}});S.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await fI(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(M$(s)).json({error:s})}});S.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await sI(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 oe(n):null;if(!a&&s&&(a=(await fe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Te.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 dA({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 fA(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 pA(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 mA(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=gI(e.query.since||null);t.json({since:n,repos:Ig(),...await yI(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var N$=async({trackId:e,cwd:t})=>{let n=e?await oe(e):null;return!n&&t&&(n=(await fe()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Te.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 N$({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 bI(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 N$({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 kI(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var kq=120,Yg=400,va=null,b$=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>Yg)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Yg} cap; a notification is a nudge, put the detail in chat`});let c=Wi(`${n||""}
|
|
717
|
+
${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 oe(a):null;if(!l&&s&&(l=(await fe()).find(h=>h.cwd&&(s===h.cwd||s.startsWith(h.cwd+Te.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,h)=>String(p).replace(/\s+/g," ").trim().slice(0,h),d={title:n?f(n,kq):l.name,body:f(i,Yg),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:va?va.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 p$(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 hc(),terms:await Kg(),problem:await f$()})});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 d$(n,r)})});S.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await oe(e.params.id))return t.status(404).json({error:"track not found"});t.json(await m$())});S.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await oe(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 h$())}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 oe(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 w$(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 Zu({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),f=null;if(c)try{await pa(u,c,{source:"create"})}catch(p){f=p?.message||String(p)}let d=null;if(l)try{await fd(u,await ua(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 oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:P$(la(e.params.id)),subtasks:Uo(e.params.id).map(Lc),pendingOffers:await tw(n)})});S.delete("/api/tracks/:id",async(e,t)=>{try{let n=await ww(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await sg(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 Lo(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 bw(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 ed(e.params.id,{freshSession:e.body?.freshSession===!0})})}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 zu(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 oe(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}
|
|
703
718
|
data: ${JSON.stringify(i)}
|
|
704
719
|
|
|
705
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await
|
|
720
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await W(n))i.seq>o&&r(i);let s=Cy(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
706
721
|
|
|
707
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});
|
|
708
|
-
`);
|
|
722
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});S.get("/api/tracks/:id/search",async(e,t)=>{let n=await oe(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=ci(await W(n)),a=Il(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 Yy(),harvest:Gy()})});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 Jy(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 xq=["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(`
|
|
723
|
+
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Nt(e.params.id,xq)})}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 td(e.params.id,{freshSession:e.body?.freshSession===!0})})}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 Nt(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 oe(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 kA(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 oe(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=Te.basename(String(e.params.file||""));if(!r||!$h(Te.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=Ih(n.cwd),s,a;try{s=await ht.promises.realpath(o),a=await ht.promises.realpath(Te.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+Te.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 vq=/^[0-9a-fA-F-]{8,64}$/;S.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await oe(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(!vq.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=en(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=lq.createInterface({input:ht.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?Vf(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 Xu(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 ow(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 Yu(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 mw({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 rw(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 lw(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 oe(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 $d(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await uw(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 Cw(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Lc(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:Uo(e.params.id).map(Lc)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await nw(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:Lc(sd(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:Lc(Mw(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Lc(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 nd(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 iw(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(P$(la(e.params.id))??{running:!1})});S.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Ew(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=Aw(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 rd(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 $w({...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 P$(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:il(),paths:db,trace:await ub({limit:n})})});var Jg=Te.resolve(S$,"..","..","client","dist");ht.existsSync(Jg)&&(S.use(jt.static(Jg)),S.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(Te.join(Jg,"index.html"),jc)));var D$=dq.createServer(S),Sq=new pq({noServer:!0}),B$=(e,t,n)=>{if(!($c(e)||xa.allowed(xa.clientIp(e))||Zg.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
709
724
|
\r
|
|
710
|
-
`),t.destroy();return}if(
|
|
725
|
+
`),t.destroy();return}if(J_(e,t,n))return;if(!z_(e,A$,{enforce:E$})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
711
726
|
\r
|
|
712
|
-
`),t.destroy();return}let r=
|
|
727
|
+
`),t.destroy();return}let r=Rb(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
713
728
|
\r
|
|
714
|
-
`),t.destroy();return}let o=r.sessionName;if(!
|
|
729
|
+
`),t.destroy();return}let o=r.sessionName;if(!Le(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
715
730
|
\r
|
|
716
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
717
|
-
`);if(!
|
|
718
|
-
`).slice(0,900)}).catch(()=>{}),e=
|
|
731
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;Sq.handleUpgrade(e,t,n,i=>_b(i,o,{cols:s,rows:a}))};D$.on("upgrade",B$);var j$=process.env.BOSTRAT_CERT_DIR||Te.join(process.env.HOME||Kd.tmpdir(),".bostrat","certs"),k$=Number(process.env.BOSTRAT_TLS_PORT)||3443,x$=Te.join(j$,"bostrat.globopi.key"),v$=Te.join(j$,"bostrat.globopi.crt");if(ht.existsSync(x$)&&ht.existsSync(v$)){let e=fq.createServer({key:ht.readFileSync(x$),cert:ht.readFileSync(v$)},S);e.on("upgrade",B$),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(k$,Vd,()=>{console.log(`[bostrat-agent] https listening on ${Vd||"0.0.0.0"}:${k$}`)})}D$.listen(g$,Vd,()=>{console.log(`[bostrat-agent] listening on ${Vd||"0.0.0.0"}:${g$}`);let e=iE(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: ${It}`),console.log(`[bostrat-agent] auto-trigger default: ${T$}%`),console.log(`[bostrat-agent] ip allowlist: ${xa.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Zg.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Tw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Rw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),kw().then(t=>t&&console.log(`[bostrat-agent] healed ${t} half-demoted transferred track(s)`)).catch(t=>console.error("[bostrat-agent] demoted-track reconcile failed:",t?.message||t)).then(()=>xw()).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)),pw().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)),Oq(),Eq(),_q(),Rq(),_x({listTracks:fe,getTrack:oe}).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)),AA()&&EA({listTracks:fe}),pb().then(t=>console.log(`[bostrat-agent] git hook shims at ${t}`)).catch(t=>console.error("[bostrat-agent] git hook shims failed (commits go unstamped):",t?.message||t)),jI(),HI(),r_({resolveTrack:oe}),p_({listTracks:fe,createTrack:Zu,archiveTrack:Lo,listRepos:Pn,resolveTrack:oe,sayToLead:Nt,answerPrompt:Yu,startLeadHandoff:nd,interruptLead:Xu,listSubtaskJobs:Uo,cancelSubtask:sd,readEvents:W,appendEvent:$,divertShip:Lg,answerPrExists:Fg,knownAgents:nc}),Tq(),Ka().then(t=>{t||console.warn(`[boot] no host tmux server on ${It} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),n$(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(va=gE({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Kd.hostname(),name:process.env.BOSTRAT_NODE_NAME||Kd.hostname(),listTracks:fe,detectAgents:Vu,sampleMetrics:R_,onTracksTransferred:Iw,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||Kd.hostname()} \u2192 plane spine sync has stalled: ${n} event(s) unanswered for ${s} min (${o} door). New Tracks won't open from the plane until it recovers; the node is redialing with backoff.`;console.error(`[plane-link] ${a}`),process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: plane sync stalled",Tags:"rotating_light"},body:a}).catch(()=>{}),b$=t},onRecovered:async({downMs:t})=>{let n=Math.round(t/6e4);process.env.BOSTRAT_NTFY_URL&&n>=5&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: plane sync recovered",Tags:"white_check_mark"},body:`Spine sync to the plane recovered after ${n} min; the backlog is delivered.`}).catch(()=>{}),b$=null},onConnected:()=>{BI()},onUpdateAvailable:async t=>{try{Xg={latest:t.latest,current:t.current},await Rn({...t,policy:xt()}),xt()==="auto"&&await _$({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>gq(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await Rn({current:t,latest:a,min:n,installKind:o,required:!0,policy:xt(),status:"available",reason:"refused"}),xt()!=="off"&&a){let i=await dd({to:a,kind:Yt,reason:"hard-floor"});i.ok?(Dc.cancel(),await Rn({current:t,latest:a,min:n,installKind:o,required:!0,policy:xt(),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:xt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),ls().catch(()=>{}),gA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),o_().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),UT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),oA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),hv().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{va?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function Tq(){Aq(),Ou().catch(()=>{}),setInterval(()=>Ou().catch(()=>{}),1e4)}function Aq(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-followup","bostrat-ship","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=Te.resolve(S$,"..","bin",e),n=Te.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!ht.existsSync(t))continue;ht.mkdirSync(n,{recursive:!0});let r=Te.join(n,e);(ht.existsSync(r)?ht.readlinkSync(r):null)!==t&&(ht.rmSync(r,{force:!0}),ht.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 Eq(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await fe(),r=await M_({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{e=!1}}};setInterval(t,6e4),t()}function _q(){let e=null,t=async()=>{try{let n=As(),r=0,o=[],s=null,a=null;try{let u=await fe(),f=await wg(u);r=f.fleet.unpricedTurns||0,o=f.fleet.unpricedModels||[],s=await O_(u,{days:7}),a=(await yg(u,{window:"7d"})).categorization}catch{}let i=[...n.problems,...a?.problems||[]];s&&!s.ok&&i.push(`${s.divergent} recent row(s) diverge from the table (implied-rate audit) \u2014 run \`npm run cost -- --audit\``);let c=o.filter(u=>u!=="unknown");c.length&&i.push(`${r} unpriced turn(s) \u2014 add rates for: ${c.join(", ")}`);let l=i.join(`
|
|
732
|
+
`);if(!i.length){e!==""&&console.log(`[pricing] table ok \u2014 rates verified ${n.tableVerifiedAt??"never"}`),e="";return}for(let u of i)console.warn(`[pricing] \u26A0 ${u}`);l!==e&&process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: cost instrument needs attention",Tags:"warning"},body:i.join(`
|
|
733
|
+
`).slice(0,900)}).catch(()=>{}),e=l}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var Iq=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,$q=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function Rq(){let e=async()=>{try{let n=await zy();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await fe()).filter(a=>!Ge(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await Vy([...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,Iq).unref()},$q).unref()}function Oq(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await fe();for(let n of t)Ge(n)||!n.boxes?.some(r=>r.state==="live")||(await zu(n.id).catch(()=>{}),await Pu(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|