bostrat-node 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bostrat-changelog +2 -2
- package/bin/bostrat-status +2 -0
- package/dist/cli.js +60 -60
- package/dist/index.js +214 -201
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await _t.mkdir(
|
|
1
|
+
var pR=Object.defineProperty;var U=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var lf=(e,t)=>{for(var n in t)pR(e,n,{get:t[n],enumerable:!0})};import{createHash as mR,randomBytes as hR,timingSafeEqual as wR}from"node:crypto";function by(){return hR(32).toString("hex")}function $a(e){let t=typeof e=="string"?e.trim():"";return t?mR("sha256").update(t).digest("hex"):null}function ky(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:wR(Buffer.from(e),Buffer.from(t))}function Ra(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(uf.set(e,(uf.get(e)||0)+1),df.has(e)&&n-df.get(e)<gR)return!1;df.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=${uf.get(e)}${o?` ${o}`:""}`),!0}async function xy(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 Dn,Bn,uf,df,gR,rs=U(()=>{Dn="BOSTRAT_HOOK_TOKEN",Bn="x-bostrat-hook";uf=new Map,df=new Map,gR=6e4});import _t from"node:fs/promises";import vy from"node:os";import vt from"node:path";import{execFile as yR}from"node:child_process";import{promisify as bR}from"node:util";function Kc(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function kR(){return process.env.CLAUDE_CONFIG_DIR?vt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):vt.join(vy.homedir(),".claude.json")}function xR(){return process.env.CLAUDE_CONFIG_DIR?vt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):vt.join(vy.homedir(),".claude","settings.json")}async function fr(e){if(!e||typeof e!="string")return;let t=vt.resolve(e),n=kR(),r=xR(),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(vt.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 "${Bn}: $${Dn}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await _t.mkdir(vt.dirname(r),{recursive:!0}),await _t.writeFile(r,JSON.stringify(i,null,2)),await vR(t)}async function vR(e){try{let t=vt.resolve(e),n=vt.join(t,".bostrat","memory","claude"),r=vt.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(vt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await _t.mkdir(n,{recursive:!0}),await _t.mkdir(vt.dirname(r),{recursive:!0}),await _t.writeFile(r,JSON.stringify(o,null,2)),await SR(t,["/.claude/settings.local.json"])}catch{}}async function SR(e,t){try{let{stdout:n}=await bR(yR)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=vt.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(vt.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 Oa=U(()=>{rs()});import ff from"node:fs/promises";import TR from"node:os";import Gc from"node:path";function AR(){let e=process.env.CODEX_HOME||Gc.join(TR.homedir(),".codex");return Gc.join(e,"config.toml")}function Sy(e){return`[projects."${e}"]`}function ER(e,t){let n=Sy(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 os(e){if(!e||typeof e!="string")return;let t=Gc.resolve(e),n=AR(),r="";try{r=await ff.readFile(n,"utf8")}catch{}if(ER(r,t))return;let o=Sy(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
|
-
`;r=r.slice(0,a)+
|
|
8
|
+
`;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await
|
|
14
|
-
`,"utf8")}var
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await ff.mkdir(Gc.dirname(n),{recursive:!0}),await ff.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Vc=U(()=>{});import Ty from"node:fs/promises";import _R from"node:os";import Ca from"node:path";function IR(e){let n=Ca.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ca.join(_R.homedir(),".cursor");return Ca.join(r,"projects",n)}async function ss(e){if(!e||typeof e!="string")return;let t=Ca.resolve(e),n=IR(t),r=Ca.join(n,".workspace-trusted");await Ty.mkdir(n,{recursive:!0}),await Ty.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var zc=U(()=>{});import mf from"node:fs/promises";import pr from"node:path";import{execFile as $R}from"node:child_process";import{promisify as RR}from"node:util";async function DR(e){try{return(await mf.stat(pr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function BR(e){let t=i=>mf.readFile(pr.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 jn(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await DR(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:pr.basename(o),branch:await BR(o)});return}if(s>=NR)return;let i;try{i=await mf.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||PR.has(c.name)||await t(pr.join(o,c.name),s+1)}for(let o of MR)await t(pr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await _e(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 jR(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function _e(e){if(!e)return null;if(pf.has(e))return pf.get(e);let t=null;try{let{stdout:n}=await OR("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=jR(n)}catch{t=null}return pf.set(e,t),t}async function as(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await jn())if((await _e(n.path))?.toLowerCase()===t)return n.path;return null}var OR,Ay,CR,MR,NR,PR,pf,mr=U(()=>{OR=RR($R),Ay=process.env.HOME||"/home/globo",CR=pr.join(process.env.BOSTRAT_STATE_DIR||pr.join(Ay,".bostrat"),"repos"),MR=[...(process.env.BOSTRAT_REPOS_ROOT||pr.join(Ay,"Documents")).split(","),CR].map(e=>e.trim()).filter(Boolean),NR=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,PR=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);pf=new Map});import LR from"node:fs";import Ey from"node:fs/promises";import Ma from"node:path";import FR from"node:os";import{fileURLToPath as UR}from"node:url";import{execFile as HR}from"node:child_process";import{promisify as WR}from"node:util";function Re(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 Yc(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Jc(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Iy.test(n))throw is("repo must be owner/name",400);let r=hf.get(n);if(r&&r.expiresAt-Date.now()>GR)return r;if(!Yc(t))throw is("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await $y(`${Re(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Ry(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw is(`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 is(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 hf.set(n,a),a}async function Na({env:e=process.env,force:t=!1}={}){if(!Yc(e))return{configured:!1,repos:[],username:null};if(!t&&so&&Date.now()-so.at<VR)return so.data;try{let n=await $y(`${Re(e)}/v1/github/repos`,{headers:Ry(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return so={at:Date.now(),data:r},r}catch(n){return so?so.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Oy(){hf.clear(),so=null}async function mn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await _e(e):null);if(!r)return{};let{token:o}=await Jc(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function wf({env:e=process.env}={}){return(await Na({env:e})).username??null}function XR(e){let[t,n]=String(e).split("/");return Ma.join(KR,t,n)}async function gf(e,{env:t=process.env}={}){let n=(...r)=>_y("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${YR()}" ${JR(t)}`),await n("config","credential.useHttpPath","true")}async function Xc(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Iy.test(String(e||"")))throw is(`bad repo slug "${e}"`,400);let r=XR(e);if(LR.existsSync(Ma.join(r,".git")))return r;let{token:o}=await Jc(e,{env:t});await Ey.mkdir(Ma.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await _y("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:zR,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Ey.rm(r,{recursive:!0,force:!0}).catch(()=>{}),is(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await gf(r,{env:t}),r}var _y,qR,KR,Iy,GR,VR,zR,$y,hf,so,Ry,is,YR,JR,lt=U(()=>{mr();_y=WR(HR),qR=process.env.BOSTRAT_STATE_DIR||Ma.join(process.env.HOME||FR.tmpdir(),".bostrat"),KR=Ma.join(qR,"repos"),Iy=/^[^/\s]+\/[^/\s]+$/,GR=5*6e4,VR=6e4,zR=10*6e4,$y=fetch,hf=new Map,so=null;Ry=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),is=(e,t)=>Object.assign(new Error(e),{status:t});YR=()=>UR(new URL("../bin/bostrat-git-credential",import.meta.url)),JR=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function My(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Py(e,t,n,r=Cy){let o=Re(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await QR(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Dy(e,{env:t=process.env,force:n=!1,timeoutMs:r=Cy}={}){if(!My(t))return{facts:[],source:"unconfigured"};let o=yf.get(e);if(!n&&o&&Date.now()-o.at<ZR)return{facts:o.facts,source:"cache"};try{let s=await Py(`/v1/memory/${encodeURIComponent(e)}`,{headers:Ny(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 yf.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(!My(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Py(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Ny(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 yf.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 ZR,Cy,yf,QR,Ny,By=U(()=>{lt();ZR=6e4,Cy=5e3,yf=new Map,QR=(...e)=>fetch(...e);Ny=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import bf from"node:fs/promises";import cs from"node:path";import eO from"node:os";import{EventEmitter as tO}from"node:events";import{randomUUID as nO}from"node:crypto";function ls(e){return e?.cwd?cs.join(e.cwd,".bostrat","events"):cs.join(kf,"events")}async function aO(e,t){if(!Qc.has(t)){let o=(await H(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Qc.set(t,o)}let n=Qc.get(t)+1;return Qc.set(t,n),n}function xf(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||!rO.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!oO.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!sO.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?cs.join(kf,"events"):ls(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let u=e.boxes.find(d=>d.name===t.boxId);o=xf(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await aO(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||nO(),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 Ly(c)&&(c=Fy(c),l=JSON.parse(c)),await bf.mkdir(r,{recursive:!0}),await bf.appendFile(jy(r,n),c+`
|
|
15
|
+
`),rt.emit("event",l),rt.emit(`track:${n}`,l),l}async function H(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?cs.join(kf,"events"):ls(e));let o;try{o=await bf.readFile(jy(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(Ly(a)?Fy(a):a))}catch{}return s}function Uy(e,t){let n=`track:${e}`;return rt.on(n,t),()=>rt.off(n,t)}var rO,oO,sO,rt,kf,jy,Qc,iO,Ly,Fy,me=U(()=>{rO=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.phase.changed","track.operation.changed","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),oO=new Set(["user","agent","orchestrator","system","peer"]),sO=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),rt=new tO;rt.setMaxListeners(0);kf=process.env.BOSTRAT_STATE_DIR||cs.join(process.env.HOME||eO.tmpdir(),".bostrat");jy=(e,t)=>cs.join(e,`${t}.ndjson`),Qc=new Map;iO=/(?<!\\)((?:\\\\)*)\\u0000/g,Ly=e=>e.includes("\\u0000"),Fy=e=>e.replace(iO,"$1\\ufffd")});import ee from"node:fs/promises";import cO from"node:os";import ke from"node:path";import{execFile as lO}from"node:child_process";import{promisify as uO}from"node:util";function tl(){return ke.join(nl,"memory","repos")}function ao(e){return e===Ln?ke.join(nl,"memory",Ln):ke.join(tl(),e)}async function Da(e){try{return JSON.parse(await ee.readFile(ke.join(e,zy),"utf8"))}catch{return{}}}async function Sf(e,t){let n=await Da(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(ke.join(e,zy),JSON.stringify(n))}function Wy(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 qy(e){try{let{stdout:t}=await dO("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Tf(e){if(!e)return[];let t=Wy(await qy(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=Wy(await qy(ke.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function rl(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 hr(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function hn(e,t=null){let n=t??await ee.readFile(e,"utf8").catch(()=>""),{fm:r}=rl(n),o=hr(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 Yy(e,t){let n=hr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Ln){let o=hr(e,"trust");if(!o||/^operator/i.test(o))return Ln}return t}function Af(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=ds(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=ds(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
22
|
-
`),o);if(a.length){let
|
|
23
|
-
`).filter(d
|
|
24
|
-
`)}await ee.mkdir(n,{recursive:!0}),await ee.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
21
|
+
`)}function Jy(e,t){let n=hr(t,"name")||e.replace(/\.md$/,""),r=hr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Ba(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 Xy(e,t){let n=ke.join(e,".bostrat","memory"),r=await Ba(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Ky.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let f=ke.join(n,d);if(o)try{if((await ee.stat(f)).mtimeMs<o-pO)continue}catch{continue}let p;try{p=await ee.readFile(f,"utf8")}catch{continue}let{fm:h}=rl(p),w=Yy(h,t);if(!w)continue;let g=ao(w),k=ke.join(g,d),x=await hn(f,p);if(l.has(g)||l.set(g,await Da(g)),(l.get(g)[d]||0)>=x)continue;let y=await hn(k).catch(()=>0);if(await ee.access(k).then(()=>!0).catch(()=>!1)&&y>=x)continue;await ee.mkdir(g,{recursive:!0}),await ee.writeFile(k,p),c++;let M=a.get(g);M||a.set(g,M={won:new Set,generated:new Map}),M.won.add(d),M.generated.set(d,Jy(d,h)),i.has(w)||i.set(w,new Set),i.get(w).add(d)}let u=await ee.readFile(ke.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,f]of a){let p=ke.join(d,"MEMORY.md"),h=await ee.readFile(p,"utf8").catch(()=>"");await ee.writeFile(p,Af(h,u,f.won,f.generated))}return Ky.set(e,s),{harvested:c,changedByKey:i}}async function Zy(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await ee.access(ke.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await Tf(r),a=await Xy(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 mO(){let e=ke.join(nl,"workspaces"),t=[];try{t=await ee.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return Zy(t.filter(n=>n.isDirectory()).map(n=>ke.join(e,n.name)))}async function gO(e,t){let n=ao(e),r=await Da(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!hO.test(l)||l==="MEMORY.md")continue;let u=ke.join(n,l),d=await ee.access(u).then(()=>!0).catch(()=>!1),f=d?await hn(u):0,p=Number(c.modified)||0;if(c.deleted){p>f&&(d&&await ee.rm(u,{force:!0}),await Sf(n,[[l,p]]),d&&(a.push(l),i++));continue}d&&f>=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=ke.join(n,"MEMORY.md"),l=await ee.readFile(c,"utf8").catch(()=>""),u=Af(l,[...s.values()].join(`
|
|
22
|
+
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
|
+
`).filter(f=>!d.has(ds(f))).join(`
|
|
24
|
+
`)}await ee.mkdir(n,{recursive:!0}),await ee.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function ol(e,t=null){let n=ao(e),r=t?.length?t:await Ba(n);if(!r.length)return{pushed:0,source:"empty"};let o=wO(await ee.readFile(ke.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=ke.join(n,a),c;try{c=await ee.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await hn(i,c),indexLine:o.get(a)??null})}return Zc(e,s)}async function yO(e,{timeoutMs:t}={}){let{facts:n,source:r}=await Dy(e,t?{timeoutMs:t}:{});n.length?await gO(e,n):r==="plane"&&await ol(e)}function bO(e){let t=e.split(`
|
|
25
|
+
`);t.length>Hy&&(t=t.slice(0,Hy));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>fO&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function xO(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=ke.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=ao(u),f=await Da(d);for(let[g,k]of Object.entries(f)){let x=ke.join(n,g);await ee.access(x).then(()=>!0).catch(()=>!1)&&await hn(x)<=k&&(await ee.rm(x,{force:!0}),o++)}let p=await Ba(d);if(!p.length)continue;await ee.mkdir(n,{recursive:!0});for(let g of p){let k=ke.join(d,g),x=ke.join(n,g),y=await hn(k),_=await hn(x).catch(()=>0);await ee.access(x).then(()=>!0).catch(()=>!1)&&_>=y||(await ee.copyFile(k,x),r++)}let h=await ee.readFile(ke.join(d,"MEMORY.md"),"utf8").catch(()=>""),w=u===Ln?"## 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 ee.writeFile(c,u).catch(()=>{});let
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=bO(i);await ee.mkdir(n,{recursive:!0}),await ee.writeFile(ke.join(n,"MEMORY.md"),c);let l=ke.join(n,"claude");return await ee.mkdir(l,{recursive:!0}),await ee.writeFile(ke.join(l,"MEMORY.md"),kO,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Qy(e){try{let t=await Tf(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,Ln];return await Promise.all(n.map(r=>yO(r,{timeoutMs:vO}))),await xO(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function eb(){return{...et,pending:us.size,queued:vf.size}}function tb(e){if(el.has(e))return el.get(e);vf.add(e);let t=Gy.then(async()=>{vf.delete(e),el.delete(e);let n=await _f(e);return et.runs++,et.lastRunAt=Date.now(),n.error?(et.errors++,et.lastError=n.error):et.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return el.set(e,t),Gy=t.then(()=>{}),t}function sl(e,{delayMs:t=SO}={}){if(!e||typeof e!="string")return;et.scheduled++;let n=us.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{us.delete(e),tb(e)},t);r.unref?.(),us.set(e,r)}async function Ef(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await ee.access(ke.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=us.get(e);r&&(clearTimeout(r),us.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([tb(e),s])}finally{clearTimeout(o)}}async function nb(e){return Pa||(Pa=(async()=>{try{Vy||(Vy=!0,await AO());let{harvested:t,changedByKey:n}=await Zy(e);for(let[r,o]of n)await ol(r,[...o]);return et.reconciles++,et.lastReconcileAt=Date.now(),et.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return et.errors++,et.lastError=t?.message||String(t),{harvested:0,error:et.lastError}}finally{Pa=null}})(),Pa)}function TO(){return ke.join(nl,"memory",".full-sweep-done")}async function rb(){let e=TO();try{let t=JSON.parse(await ee.readFile(e,"utf8"));return et.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...et.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await mO();for(let[o,s]of n)await ol(o,[...s]);let r={at:Date.now(),harvested:t};return await ee.mkdir(ke.dirname(e),{recursive:!0}),await ee.writeFile(e,JSON.stringify(r)),et.backfill=r,{ran:!0,...r}}catch(t){return et.errors++,et.lastError=t?.message||String(t),{ran:!1,error:et.lastError}}}async function AO(){let e=0,t=[];try{t=await ee.readdir(tl(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=ke.join(tl(),n.name),o=ao(Ln),s=new Set,a=new Map;for(let p of await Ba(r)){let h=ke.join(r,p),w;try{w=await ee.readFile(h,"utf8")}catch{continue}let{fm:g}=rl(w);if(Yy(g,n.name)!==Ln)continue;let k=ke.join(o,p),x=await hn(k).catch(()=>0),y=await hn(h,w);await ee.mkdir(o,{recursive:!0}),y>x&&await ee.writeFile(k,w),await ee.rm(h,{force:!0}),s.add(p),a.set(p,Jy(p,g)),e++}if(!s.size)continue;let i=Date.now();await Sf(r,[...s].map(p=>[p,i])),await Zc(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=ke.join(r,"MEMORY.md"),l=await ee.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(p=>!s.has(ds(p))).join(`
|
|
34
|
+
`);await ee.writeFile(c,u).catch(()=>{});let d=ke.join(o,"MEMORY.md"),f=await ee.readFile(d,"utf8").catch(()=>"");await ee.writeFile(d,Af(f,l,s,a))}return{moved:e}}async function ob(){let e=[],t=[Ln];try{for(let n of await ee.readdir(tl(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=ao(n),o=await Ba(r),s=await Da(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await ee.readFile(ke.join(r,i),"utf8").catch(()=>""),{fm:l}=rl(c);a.push({file:i,name:hr(l,"name")||i.replace(/\.md$/,""),description:hr(l,"description")||"",scope:hr(l,"scope")||null,modified:await hn(ke.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function sb(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=ao(e),r=ke.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 Sf(n,[[t,s]]);let a=ke.join(n,"MEMORY.md"),i=await ee.readFile(a,"utf8").catch(()=>"");await ee.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>ds(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 _f(e){try{let t=await Tf(e),{harvested:n,changedByKey:r}=await Xy(e,t[0]||null);for(let[o,s]of r)await ol(o,[...s]);return n>0&&await _O(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function _O(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,EO)}}await $({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var dO,nl,Hy,fO,Ln,zy,ds,Ky,pO,hO,wO,kO,vO,SO,us,vf,Gy,et,el,Vy,Pa,EO,ja=U(()=>{By();me();dO=uO(lO),nl=process.env.BOSTRAT_STATE_DIR||ke.join(process.env.HOME||cO.tmpdir(),".bostrat"),Hy=200,fO=25*1024;Ln="operator";zy=".tombstones.json";ds=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Ky=new Map,pO=2e3;hO=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,wO=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=ds(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};kO=`# 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
|
+
`;vO=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;SO=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,us=new Map,vf=new Set,Gy=Promise.resolve(),et={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};el=new Map;Vy=!1,Pa=null;EO=20});import If from"node:fs/promises";import IO from"node:path";async function St(e,t,{mode:n}={}){await If.mkdir(IO.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++$O}.tmp`;await If.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await If.rename(r,e)}var $O,wr=U(()=>{$O=0});import ub from"node:fs/promises";import db from"node:os";import fb from"node:path";function je(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ua(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),RO);try{return await DO(`${Re(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function mb(){return il||($f||=ub.readFile(pb(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>il={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>$f=null),$f)}async function cl(){await St(pb(),il).catch(()=>{})}function cb(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Of(e){let t=await mb(),n=cb(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=>cb(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>ab&&(t.ops=t.ops.slice(-ab)),await cl()}function lb(){al||(al=setTimeout(()=>{al=null,fs().catch(()=>{})},BO),al.unref?.())}function Rf(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function LO(e,t=process.env){if(e.op==="record"){let n=await Ua(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Fa(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:PO()}]})},t);if(!n.ok)throw Rf(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 ub.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ua(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Fa(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Rf(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 Ua(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Fa(t)},t);if(!n.ok)throw Rf(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function fs({env:e=process.env,log:t=console,now:n=Date.now()}={}){return je(e)?La||(La=(async()=>{let r=await mb();FO(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await LO(a,e)}catch(c){if(!c?.refused)return lb(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<CO)return lb(),await cl(),{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>ib&&(r.parked=r.parked.slice(-ib)),s++,await cl();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await cl()}return{flushed:o,parked:s,pending:0}})().finally(()=>{La=null}),La):{flushed:0,source:"unconfigured"}}function FO(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=jO);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 wt(e,t,{trackId:n=null,env:r=process.env}={}){return je(r)?(await Of({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),fs({env:r})):{queued:!1,source:"unconfigured"}}async function gr(e,t,n,r,o,{env:s=process.env}={}){return je(s)?(await Of({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),fs({env:s})):{queued:!1,source:"unconfigured"}}async function Zt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return je(r)?(await Of({op:"delete",family:e,id:t,deletedAt:n}),fs({env:r})):{queued:!1,source:"unconfigured"}}async function ot(e,{env:t=process.env}={}){if(!je(t))return{records:[],source:"unconfigured"};try{let n=await Ua(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Fa(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 Ha(e,t,n,r,{env:o=process.env}={}){if(!je(o))return null;try{let s=await Ua(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Fa(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 RO,OO,ab,CO,ib,MO,NO,pb,PO,DO,BO,jO,Fa,il,$f,al,La,yr=U(()=>{lt();wr();RO=1e4,OO=3e4,ab=2e3,CO=5,ib=100,MO=60*6e4,NO=()=>process.env.BOSTRAT_STATE_DIR||fb.join(process.env.HOME||db.tmpdir(),".bostrat"),pb=()=>fb.join(NO(),"content-outbox.json"),PO=()=>process.env.BOSTRAT_NODE_ID||db.hostname(),DO=(...e)=>fetch(...e),BO=OO,jO=MO;Fa=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});il=null,$f=null,al=null,La=null});import Cf from"node:fs/promises";import hb from"node:path";async function wb(e,t,{box:n=null,agent:r=null}={}){if(!e||!je())return{pushed:!1};try{let o=Date.now();return await wt("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await gr("checkpoints",e,o,"md",t),Wa=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function KO(){if(Wa&&Date.now()-Wa.at<qO)return{records:Wa.records,source:"plane"};let e=await ot("checkpoints");return e.source==="plane"&&(Wa={at:Date.now(),records:e.records}),e}async function ll(e){try{let t=WO(e);if(!t||!je())return{restored:!1};let n=hb.join(e,UO,HO);if(await Cf.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await KO();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 Ha("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Cf.mkdir(hb.dirname(n),{recursive:!0}),await Cf.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var UO,HO,WO,qO,Wa,ul=U(()=>{yr();UO=".bostrat/handoff",HO="latest.md",WO=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;qO=6e4,Wa=null});import qa from"node:fs/promises";import GO from"node:os";import yb from"node:path";function io(e){let t=String(e??"");for(let n of VO)t=t.replace(n,"\u2039redacted\u203A");return t}function dl(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function zO(e){try{await qa.mkdir(Mf,{recursive:!0}),await qa.appendFile(ps,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await qa.readFile(ps,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>gb&&await qa.writeFile(ps,`${n.slice(-gb).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 ${ps}: ${t?.message||t}`)}}async function ce(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&&dl())try{t.paneTail=io((await Pe(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(dl()||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 zO(t)}async function bb({limit:e=80}={}){try{return(await qa.readFile(ps,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=io(o.paneTail)),o}catch{return{raw:io(r)}}})}catch{return[]}}var VO,Mf,ps,gb,kb,wn=U(()=>{ut();VO=[/[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];Mf=process.env.BOSTRAT_DEBUG_DIR||yb.join(GO.homedir(),".bostrat","debug"),ps=yb.join(Mf,"handoff.jsonl"),gb=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;kb={DEBUG_DIR:Mf,LOG_FILE:ps}});import fl from"node:fs/promises";import Nf from"node:path";import YO from"node:os";async function vb(e=xb){await fl.mkdir(e,{recursive:!0});for(let t of XO){let n=t==="prepare-commit-msg"?ZO:t==="post-commit"?QO:`${Pf}exit 0
|
|
49
|
+
`,r=Nf.join(e,t),o=null;try{o=await fl.readFile(r,"utf8")}catch{}o!==n&&await fl.writeFile(r,n,{mode:493}),await fl.chmod(r,493).catch(()=>{})}return e}function Sb({nodeUrl:e,dir:t=xb,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",`${Ka}=${e}`]}function Tb(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Ka,JO,xb,XO,Pf,ZO,QO,Df=U(()=>{rs();Ka="BOSTRAT_NODE_URL",JO=process.env.BOSTRAT_STATE_DIR||Nf.join(process.env.HOME||YO.tmpdir(),".bostrat"),xb=Nf.join(JO,"git-hooks"),XO=["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"],Pf=`#!/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
|
+
`,ZO=`${Pf}
|
|
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 "$${Dn}" ] && exit 0
|
|
68
|
+
[ -z "$${Ka}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${Bn}: $${Dn}" "$${Ka}/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
|
+
`,QO=`${Pf}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
|
-
[ -z "$${
|
|
79
|
-
[ -z "$${
|
|
78
|
+
[ -z "$${Dn}" ] && exit 0
|
|
79
|
+
[ -z "$${Ka}" ] && 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 "$${Ka}/api/git/committed" \\
|
|
83
|
+
-H 'content-type: application/json' -H "${Bn}: $${Dn}" --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,
|
|
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=
|
|
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
|
|
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 Eb}from"node:child_process";import{promisify as eC}from"node:util";import ml from"node:fs";import{randomUUID as _b}from"node:crypto";function ws(e){return new Promise(t=>setTimeout(t,e))}function pl(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 Ib(e){return`/tmp/tmux-${e}/default`}function tC({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=Ib(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Va(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Ga}=1`]}function za(e,t,n){let r=co(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 co(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function br(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function oC(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:co(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function $b(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await Rb(e,{loginShell:t});return r===hl?!0:r===ms?!1:n}async function Rb(e,{loginShell:t=!1,timeoutMs:n=Bf}={}){if(!e)return ms;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Oe(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),hl}catch(s){return s?.killed||s?.signal?hs:s?.code===1||s?.code===127?ms:hs}}async function wl(e,{retries:t=sC,sleep:n=aC,probe:r=Rb}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Bf});for(let a=0;s===hs&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Bf*2});return s}function Z(...e){return["-S",It,...e]}async function kr(){if(!ml.existsSync(It))return!1;try{return await Oe(Y,Z("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ya(){if(await kr())return!0;try{ml.mkdirSync(It.replace(/\/[^/]+$/,""),{recursive:!0}),await Oe(Y,Z("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await kr()}function Le(e){return typeof e=="string"&&iC.test(e)}async function Qt(){if(!ml.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 Oe(Y,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,d,f,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:d||null,agent:f||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!rC.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function xe(e){return(await Qt()).find(n=>n.name===e)||null}function uC(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=lC&&cC.test(t)}function gl(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Ob(e){try{let{stdout:t}=await Oe(Y,Z("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function pC(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[],r=s=>/[A-Za-z0-9_]/.test(s);for(let s=1;s<=t.length-3;s++)r(t[s-1])===r(t[s])&&n.push(t.slice(s));if(!n.length)return null;let o=n.map(s=>s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${o.join("|")})`)}async function mC(e,t){let n=Date.now()+Ja(),r=pC(t),o=!1,s=0,a=0;for(;;){if(await ws(dC),s+=1,!await Ob(e))return;let i=await Pe(e,{lines:15}),c=!o&&fC.test(i)&&!!r?.test(i),l=!o&&!!r?.test(i);if(!c&&!l&&s>1&&!await Fn(e)){if(a+=1,a>1)return}else c||(a=0);if(c){o=!0,await ce({flow:"spawn",phase:"launch-retry",sessionName:e,agent:br(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Oe(Y,Z("send-keys","-t",e,"C-u")),await Oe(Y,Z("send-keys","-t",e,"-l",t)),await Oe(Y,Z("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await ce({flow:"spawn",phase:"launch-failed",sessionName:e,agent:br(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function xr(e,t=nC,n={}){if(!uC(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=gl(r);if(!Le(o))throw new Error("resulting tmux session name is invalid");if(!await Ya())throw new Error(`host tmux server not running on ${It} and auto-start failed \u2014 start one: ${Y} -S ${It} new-session -d -s keeper "sleep 2147483647"`);if(await Ob(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||jf,a=n.agent||br(s),i=!!n.resumeSessionId,c=n.resumeSessionId||_b(),l=i?s:za(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&co(s),f=n.firstRun===!0;f||(d&&await fr(t),a==="codex"&&await os(t),a==="cursor"&&await ss(t),await Qy(t),n.freshWorkspace!==!0&&await ll(t));let p=d&&!f?Kc():null,h=by(),w=["-e",`${Dn}=${h}`];w.push(...Sb({nodeUrl:Tb()})),p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),d&&w.push(...Va());try{await Oe(Y,Z("new-session","-d","-s",o,"-c",t,...w))}catch(g){throw pl(p?pl(g,p):g,h)}return await lo(o),await Oe(Y,Z("set-option","-t",o,"@bostrat-session","1")),await Oe(Y,Z("set-option","-t",o,"@bostrat-title",r)),await Oe(Y,Z("set-option","-t",o,"@bostrat-sid",c)),await Oe(Y,Z("set-option","-t",o,"@bostrat-agent",a)),await Oe(Y,Z("send-keys","-t",o,l,"Enter")),oC(l)&&mC(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:h}}async function en(e){if(!Le(e))throw new Error("invalid session name");await Oe(Y,Z("kill-session","-t",e))}async function Cb(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 Oe(Y,n)}catch{}}async function Pe(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 Oe(Y,n);return String(r||"")}catch{return""}}async function Mb(e){if(!Le(e))return 0;try{let{stdout:t}=await Oe(Y,Z("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
88
|
+
`).filter(Boolean).length}catch{return 0}}async function lo(e,{cols:t=gC,rows:n=yC}={}){if(!Le(e))return!0;if(await Mb(e))return!1;try{let r=await Nb(e);if(r.cols===t&&r.rows===n)return!0;await Oe(Y,Z("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Oe(Y,Z("set-window-option","-t",e,"window-size","latest")),await ws(80)}catch{}return!0}async function bC(e){if(!Le(e))return 0;try{let{stdout:t}=await Oe(Y,Z("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Fn(e){let t=await bC(e);if(!t)return!1;try{return await Oe("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function Nb(e){if(!Le(e))return{cols:0,rows:0};try{let{stdout:t}=await Oe(Y,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 yl(e,{cols:t=hC,rows:n=wC}={}){if(Le(e)&&!await Mb(e))try{let r=await Nb(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Oe(Y,Z("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Oe(Y,Z("set-window-option","-t",e,"window-size","latest")),await ws(80))}catch{}}async function Un(e){return await yl(e),Pe(e,{lines:tt})}async function We(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=>kC.has(s));for(let s=0;s<o.length;s++)await Oe(Y,Z("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await ws(n);return ce({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function gs(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 Oe(Y,Z("send-keys","-t",e,"-N",String(r),"BSpace")),ce({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function kl(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 Oe(Y,Z("send-keys","-t",e,"-l",s)),n>0&&await ws(n);ce({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function xC(e,t){return new Promise((n,r)=>{let o=Eb(Y,Z(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function vC(e,t){let n=`bostrat-${_b().slice(0,8)}`;await xC(["load-buffer","-b",n,"-"],t),await Oe(Y,Z("paste-buffer","-p","-d","-b",n,"-t",e))}async function nt(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 vC(e,t):await Oe(Y,Z("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await ws(a),await Oe(Y,Z("send-keys","-t",e,"Enter"))}ce({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 Oe,Y,Ab,It,nC,jf,Ga,Lf,rC,hl,ms,hs,Bf,sC,aC,Ff,iC,cC,lC,Ja,dC,fC,hC,wC,tt,gC,yC,kC,bl,ut=U(()=>{Oa();Vc();zc();ja();ul();wn();rs();Df();Oe=eC(Eb);Y=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";Ab=typeof process.getuid=="function"?process.getuid():0,It=tC({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return ml.existsSync(Ib(Ab))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Ab}),nC=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",jf=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ga="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";Lf="bostrat-usage-probe",rC=new Set([Lf]);hl="present",ms="absent",hs="unknown",Bf=5e3,sC=[250,1e3,3e3],aC=e=>new Promise(t=>setTimeout(t,e));Ff={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"};iC=/^[A-Za-z0-9_.-]{1,64}$/;cC=/^[A-Za-z0-9 ._↻-]+$/,lC=64;Ja=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,dC=1e3,fC=/not found|Unknown command/i;hC=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,wC=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,tt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,gC=Number(process.env.BOSTRAT_BOX_COLS)||120,yC=Number(process.env.BOSTRAT_BOX_ROWS)||40;kC=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);bl=1600});import Xa from"node:fs";import IC from"node:fs/promises";import ys from"node:path";import $C from"node:os";function Wf(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function qf(e){let t=ys.join(Hf,Wf(e)),n;try{n=Xa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=ys.join(t,o);try{let{mtimeMs:a}=Xa.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function tn(e,t){if(!t)return null;let n=ys.join(Hf,Wf(e),`${t}.jsonl`);return Xa.existsSync(n)?n:null}function Ub(e){if(!e)return[];let t=ys.join(Hf,Wf(e)),n;try{n=Xa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Xa.statSync(ys.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Tt(e,t=131072){let n=await IC.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 OC(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 Za(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=tn(e,t),o=!0,!r)return null}else if(n){if(r=qf(e),o=!1,!r)return null}else return null;let s;try{s=OC(await Tt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Fb,s.model),i=Fb[s.model]||RC;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 Fb,RC,Hf,nn=U(()=>{Fb={"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},RC=2e5,Hf=process.env.BOSTRAT_CLAUDE_PROJECTS||ys.join($C.homedir(),".claude","projects")});function CC(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 Qa(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 ei(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 Ce(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(qb(o)){t.has(o)&&r++;continue}n.push(o)}return n}function Kb(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 PC(e){let t=Kb(e),n=dt(Ce(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${qe(n)}`:`Read lines ${t.from}\u2013${t.to} of ${qe(n)}`:e.includes("-i")&&n?`Edit ${qe(n)}`:null}function Kf(e){if(e.includes("--files")){let s=dt(Ce(e,Hb)[0]);return s&&s!=="."?`List files in ${bs(s)}`:"List files"}let t=Ce(e,Hb),n=t[0];if(!n)return null;let r=dt(t.slice(1).find(s=>s!==".")),o=ks(n,40);return r?`Search for \u201C${o}\u201D in ${bs(r)}`:`Search for \u201C${o}\u201D`}function Gf(e){let t=Ce(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 Vf(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=Ce(e)[0];return n?`Run ${qe(n)}`:null}function DC(e){let t=Ce(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 Wb(e){return e.find(t=>/^https?:\/\//.test(t))||null}function LC(e){let t=ei(Qa(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(qe(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return zf(t[2]);let n=qe(t[0]);if(jC.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=BC[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function zf(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=CC(t);for(let i=0;i<r.length-1;i++){let c=ei(Qa(r[i])),l=ei(Qa(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(qe(c[0]))&&qe(l[0])==="sed"){let u=Kb(l),d=Ce(c)[0];if(u&&d)return ks(u.from===u.to?`Read line ${u.from} of ${qe(d)}`:`Read lines ${u.from}\u2013${u.to} of ${qe(d)}`,88)}}let o=[];for(let i of r){let c=LC(i);c&&o.push({d:c,head:qe(ei(Qa(i))[0]||""),sub:Ce(ei(Qa(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":ks(s?s.d:o[0].d,88)}var ks,dt,qe,bs,qb,Hb,MC,NC,BC,jC,Gb=U(()=>{ks=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,dt=e=>e&&!/[$`]/.test(e)?e:null,qe=e=>e.replace(/\/+$/,"").split("/").pop()||e,bs=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},qb=e=>e.startsWith("-"),Hb=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);MC={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 ${qe(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"},NC={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"};BC={git:e=>{let t=Ce(e,new Set(["-C","-c"])),n=MC[t[0]];return n?n(t,e):null},gh:e=>{let t=Ce(e),n=NC[t[0]];return n?n(t):null},sed:PC,rg:Kf,grep:Kf,egrep:Kf,nl:e=>{let t=dt(Ce(e)[0]);return t?`Read ${qe(t)}`:null},cat:e=>{let t=dt(Ce(e)[0]);return t?`Read ${qe(t)}`:null},head:e=>{let t=dt(Ce(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${qe(t)}`:null},tail:e=>{let t=dt(Ce(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${qe(t)}`:null},wc:e=>{let t=dt(Ce(e)[0]);return t?`Count lines in ${qe(t)}`:"Count input lines"},ls:e=>{let t=dt(Ce(e)[0]);return t&&t!=="."?`List files in ${bs(t)}`:"List files"},find:e=>dt(e[1])&&!qb(e[1])&&e[1]!=="."?`Find files in ${bs(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=dt(Ce(e)[0]);return t?`Create directory ${bs(t)}`:null},touch:e=>{let t=dt(Ce(e)[0]);return t?`Create ${qe(t)}`:null},rm:e=>{let t=dt(Ce(e)[0]);return t?`Delete ${bs(t)}`:null},mv:e=>{let t=dt(Ce(e)[0]);return t?`Move ${qe(t)}`:null},cp:e=>{let t=dt(Ce(e)[0]);return t?`Copy ${qe(t)}`:null},chmod:e=>{let t=dt(Ce(e)[1]);return t?`Change permissions on ${qe(t)}`:null},diff:e=>{let t=Ce(e);return t.length>=2?`Compare ${qe(t[0])} and ${qe(t[1])}`:null},curl:e=>{let t=Wb(e);return t?`Fetch ${ks(t,56)}`:"Fetch a URL"},wget:e=>{let t=Wb(e);return t?`Fetch ${ks(t,56)}`:"Fetch a URL"},npm:Gf,pnpm:Gf,yarn:Gf,npx:e=>{let t=Ce(e)[0];return t?`Run ${t}`:null},node:e=>Vf(e,"Node"),python:e=>Vf(e,"Python"),python3:e=>Vf(e,"Python"),make:e=>{let t=Ce(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:DC,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=Ce(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=Ce(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Ce(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},jC=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function xs(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 vr(e,t=""){let n=t.trim();return n||zf(e)||xs(e)}function Yf(e,t){if(e){if(Array.isArray(e)){for(let n of e)Yf(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Jf.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))Yf(n,t)}}}async function Vb(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await Tt(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(Yf(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:vr(d.command,d.description),ts:u})}return o.slice(-n)}function zb(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)),d=u===-1?r.length:u,f=r.slice(s,d);if(s=d,!f.length)continue;let p=f.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 Jf,vs=U(()=>{nn();Gb();Jf=new Set(["Bash"])});import Yb from"node:fs";import Zf from"node:fs/promises";import Xf from"node:path";import FC from"node:os";async function Jb(e){try{return await Zf.realpath(e)}catch{return e}}function HC(e=UC){let t=[],n=r=>{let o;try{o=Yb.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Xf.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:Yb.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function WC(e,t=1048576){let n;try{n=await Zf.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 qC(e){let t=await WC(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 uo(e,t){if(!e&&!t)return null;let n=HC();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Jb(e);for(let o of n){let s=await qC(o.file);if(s&&(s===e||await Jb(s)===r))return o}return null}async function Xb(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await uo(e,t);if(!o)return[];let s;try{s=await Tt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
|
|
95
|
+
`)){let p=f.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 x=h.payload?.type;x==="task_started"?(l=!0,u=!1,d=h.payload?.turn_id||null):x==="task_complete"&&l&&(l=!1,!u&&h.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:fo,sourceId:h.payload?.turn_id||d||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,Qf.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let g=GC(w.content),k=c.length?c.slice():null;c=[],!(!g&&!k)&&a.push({role:"assistant",text:g,tools:k||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}function VC(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 zC(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 YC(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=zC(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Zb(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await uo(e,t);if(!o)return[];let s;try{s=await Tt(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 d=[];if(u.type==="function_call"&&ti.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let h=VC(p);h&&(d=[h])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=YC(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((p,h)=>{a.push({id:f?h?`${f}:${h}`:f:null,tool:u.name,command:p,description:"",summary:vr(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Qb(e,t){let n=await uo(e,t);if(!n)return null;try{return Math.round((await Zf.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var UC,KC,GC,ti,Qf,fo,po=U(()=>{nn();vs();UC=process.env.BOSTRAT_CODEX_SESSIONS||Xf.join(process.env.CODEX_HOME||Xf.join(FC.homedir(),".codex"),"sessions");KC=new Set(["output_text","text"]),GC=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&KC.has(t.type)).map(t=>t.text).join(`
|
|
98
|
+
`).trim():"";ti=new Set(["exec_command","shell","exec"]),Qf=new Set(["wait","write_stdin"]),fo="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 ni({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&ek.has(r))return ek.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 tk(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||ni({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===fo?"other":ni({text:t}),code:null,status:null,requestId:null}}return null}function nk(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:ni({text:n}),code:null,status:JC(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function JC(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var XK,ek,vl=U(()=>{po();XK=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),ek=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 XC(e,t){return!e||!t?null:tn(e,t)}function ZC(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 QC(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 eM(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function ep(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 rk(e,t){if(!e.uuid)return null;let n=ep(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 tp(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await Tt(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"&&tM.test(g)&&s.set(w.id,g.split("/").pop())}if(eM(l.content)){let w=rk(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=ZC(l.content),f=u==="assistant"?QC(l.content):null,p=u==="user"?rk(c,s):null;if(!d&&!f&&!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:ni({error:c.error,status:c.apiErrorStatus,text:d})}:null;o.push({role:u,text:d,tools:f||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:h||void 0})}return o.slice(-n)}async function ok(e,t,{turns:n=16}={}){let r=XC(e,t);if(!r)return"";let o=await tp(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}:** ${nM(s.text||c,700)} ${i}`.trim()}).join(`
|
|
101
|
+
|
|
102
|
+
`):""}var tM,nM,Sl=U(()=>{vl();nn();tM=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;nM=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import rM from"node:fs/promises";import{execFile as oM}from"node:child_process";import{promisify as sM}from"node:util";function ck(e){rp=typeof e=="function"?e:null}function op(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function lk({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||sk.has(r)||(sk.add(r),ak=ak.then(()=>iM({sessionName:e,from:t,to:r})).catch(()=>{}))}async function iM({sessionName:e,from:t,to:n}){await ce({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await np("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await rM.realpath(r),s=op(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await np("npm",["install","-g","@openai/codex"],{env:a,timeout:aM});let i=(await np(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await ce({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await ik({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await ce({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await ik({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function ik(e){if(rp)try{await rp(e)}catch{}}var np,aM,sk,ak,rp,Tl=U(()=>{wn();np=sM(oM),aM=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,sk=new Set,ak=Promise.resolve(),rp=null});function lM(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
103
|
+
`);if(!uk.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(cM);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"&&uk.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 mo(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
104
|
+
`);for(let r=n.length-1;r>=0;r--)if(pk.test(n[r]))return n.slice(r).join(`
|
|
105
|
+
`);return t}function uM(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 dM(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 fM(e,t=""){let n=mo(e);if(ri.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(dk);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let p=r[f].trim();if(p){if(dk.test(r[f])||/^Press enter/.test(p))break;d=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!pk.test(r[l])&&uM(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=dM(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function hM(e,t=""){let n=mo(e);if(ri.test(n)||!pM.test(n))return null;let r=n.trimEnd().split(`
|
|
107
|
+
`).pop()||"";return mM.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 gM(e,t=""){let n=mo(e);if(ri.test(n)||!fk.test(n))return null;let r=n.split(`
|
|
108
|
+
`),o=r.findIndex(i=>fk.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(wM);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 yM(e,t=""){let n=mo(e);return ri.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 bM(e,t=""){let n=mo(e);return ri.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 kM(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||sp.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 vM(e,t=""){let n=String(e);if(!xM.test(n)||sp.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 SM(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||sp.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 mk(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=hM(t,n)||gM(t,n)||yM(t,n)||bM(t,n)||fM(t,n);if(r)return r}if(e==="cursor"){let r=vM(t,n)||SM(t,n)||kM(t,n);if(r)return r}return lM(t,{provider:e,box:n})}function hk(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 cM,uk,ri,pk,dk,pM,mM,fk,wM,sp,xM,Al=U(()=>{cM=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,uk=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;ri=/(^|\n)\s*›/,pk=/^\s*Welcome to Codex\b/;dk=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;pM=/Successfully logged in\./,mM=/^\s*Press Enter to continue\.\s*$/;fk=/Enter this one-time code/,wM=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;sp=/Run Everything|Add a follow-up|Plan, search, build anything/;xM=/click this link to log in|Signing in with the browser/});function _M(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(AM.test(r)||EM.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 CM(e){let t=String(e||"");if(!ze.codex.block.test(t))return null;let n=mo(t);if(!ze.codex.block.test(n))return null;let r=n.match(OM);return{from:r?.[1]||null,to:r?.[2]||null}}function gn(e){return new Promise(t=>setTimeout(t,e))}function ap(e,t){let n=Date.now(),r=MM();for(let[s,a]of _l)n-a>=r&&_l.delete(s);let o=`${e}::${t}`;return _l.has(o)?!1:(_l.set(o,n),!0)}function bk(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(yk.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
113
|
+
`)}function PM(e){let t=0;for(let n of String(e).split(`
|
|
114
|
+
`))yk.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function ip(e,t){let n=ze[e];if(!n?.busy)return{state:"idle",reason:null};if(lp(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(bk(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=PM(t);return e==="claude"&&r>0&&r<NM?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function cp(e,t){return ip(e,t).state}function kk(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
115
|
+
`).slice(-BM).join(`
|
|
116
|
+
`);return DM.test(n)}function Ts(e,t){return cp(e,t)==="busy"}function Il(e,t){return cp(e,t)!=="idle"}function As(e,t){let n=ze[e];return n?.up?n.up.test(t):!0}function jM(e,t){let n=ze[e];return n?lp(e,t)?!1:n.up.test(t)&&cp(e,t)==="idle":!0}function lp(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 up(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 xk(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function wk(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 gk(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await lo(e).catch(()=>{});;){let l=up(await Pe(e),o);if(jM(t,l)){await s?.(),await gn(Ss);return}if(ze[t]?.up&&!ze[t].up.test(l))if(await Fn(e)){if(c??=Date.now(),Date.now()-c>=Ja()){let u=wk(t);throw ce({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"&&RM.test(l)&&!ze.claude.up.test(l)){ce({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="codex"&&$M.test(l)&&!ze.codex.up.test(l)&&i<3){if(!ap(e,"codex-trust")){await gn(El);continue}i+=1,await ce({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await nt(e,"",{enter:!0,kind:"codex-trust-accept"}),await gn(Ss);continue}if(t==="cursor"&&IM.test(l)&&!ze.cursor.up.test(l)&&i<3){if(!ap(e,"cursor-trust")){await gn(El);continue}i+=1,await ce({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await nt(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await gn(Ss);continue}if(t==="codex"&&i<3){let u=CM(l);if(u){if(!ap(e,`codex-update:${u.from}->${u.to}`)){await gn(El);continue}i+=1,await ce({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await nt(e,"3",{enter:!1,kind:"codex-update-skip"}),lk({sessionName:e,from:u.from,to:u.to}),await gn(Ss);continue}}if(Date.now()>a){if(lp(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'),ce({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await Fn(e)){let u=wk(t);throw ce({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.`),ce({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(bk(ze[t],l))}).catch(()=>{}),await gn(Ss);return}await gn(El)}}function dp({agent:e,agentCmd:t}={}){let n=t?br(t):br(e||"");return{label:n,realClaude:t?co(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 vk({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=dp({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function oi({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}={}){let{realCodex:s,realCursor:a}=dp({agent:n,agentCmd:r});return!t||!s&&!a?null:Sr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).catch(()=>{})}async function Sr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=TM(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:f}=dp({agent:r,agentCmd:o});if(u&&e&&await fr(e),d&&e&&await os(e),f&&e&&await ss(e),(u||d||f)&&n){await gk(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 gk(n,p,s,{launchEcho:i});return}await gn(Ss)}var TM,El,Ss,AM,EM,ze,IM,$M,RM,OM,MM,_l,NM,yk,DM,BM,si=U(()=>{ut();Oa();Vc();zc();Tl();Al();wn();TM=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,El=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Ss=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,AM=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,EM=/^\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:_M},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/}},IM=/Trust this workspace|\[a\]\s*Trust/,$M=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,RM=/Resume from summary/i,OM=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;MM=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,_l=new Map;NM=68,yk=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;DM=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,BM=15});function HM(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
119
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=UM}function yn(e,t){return e==="codex"?{enter:!0,submitDelayMs:LM,...HM(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:FM}:{enter:!0}}var LM,FM,UM,ai=U(()=>{LM=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),FM=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),UM=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function ii(e,t){if(e==="codex")return VM(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(Sk.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Sk.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(WM);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 VM(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]){(qM.test(t[o])||KM.test(t[o]))&&!fp.test(t[o])?n=o:fp.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(fp);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(GM.test(s))return null;r.unshift(s)}return null}function pp(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Ve(n.replace(zM,(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 Tk(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 Ak(e,t,n,{busy:r=!1}={}){let o=ii(e,n);return o&&$l(t,o)||o&&e==="codex"&&pp(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function $l(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 Sk,WM,fp,qM,KM,GM,zM,Rl=U(()=>{Sk=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,WM=/^❯[ ]?(.*)$/,fp=/^\s*›[ ]?(.*)$/,qM=/\S+ · \S/,KM=/^\s*(?:\? for shortcuts|tab to queue message)\b/,GM=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;zM=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function li({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say"},a={}){if(!YM.has(t))return!0;let i=a.waitForUp??vk({agent:t,agentCmd:n}),c=a.capture||(_=>Pe(_,{lines:tt})),l=a.pressEnter||(_=>We(_,["Enter"],{kind:"submit-retry"})),u=a.sleep||QM,d=a.now||Date.now,f=a.log||(_=>ce(_).catch(()=>{})),p=a.maxMs??ZM(),h=d()+p,w={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},g=0,k=0,x=0,y=!1;for(await u(ci[0]);;){let _=await c(e).catch(()=>""),M=As(t,_),j=Ts(t,_),L=Ak(t,r,_,{busy:j});if(L==="submitted")return!0;if(i&&(!M||L==="stuck"&&j)){if(d()>=h)return L==="stuck"?(f({...w,phase:"submit-failed",reason:"stuck-at-deadline",retries:g}),!1):(f({...w,phase:"submit-unverified",reason:y?"cli-down":"cli-never-up",retries:g,waitedMs:p}),!0);await u(JM);continue}if(y=!0,L==="stuck"){if(g>=XM)return f({...w,phase:"submit-failed",retries:g}),!1;g+=1,k=0,x=0,await l(e),f({...w,phase:"submit-retry",attempt:g}),await u(ci[0]);continue}if(k+=1,k>=ci.length||d()>=h)return!0;x+=1,await u(ci[Math.min(x,ci.length-1)])}}var YM,ci,JM,XM,ZM,QM,mp=U(()=>{ut();si();Rl();wn();YM=new Set(["claude","codex"]),ci=[250,450,700],JM=500,XM=2,ZM=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},QM=e=>new Promise(t=>setTimeout(t,e))});function Ek(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function Hn(e){let t=Ek(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of eN){let o=0;for(let s of tN[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function _k(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))nN.has(r)&&(t+=o||0),rN.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function ui(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 Ik(e){let t=Ek(e).trim();return t?sN.test(t)?!0:t.split(/\s+/).length<=oN:!1}var eN,tN,nN,rN,oN,sN,Ol=U(()=>{eN=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],tN={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/,/\bhow often\b/,/\bdo we (currently|already)\b/,/\bwould it be (valuable|useful|worth)\b/,/\bis it possible\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/,/\bfail(ed|ing|s)\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)/,/\b(linked[ -]?in|social( media)?) post\b/],"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/,/\b(unzip|decompress|untar)\b/]};nN=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),rN=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);oN=12,sN=/^\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 Ck(e){let t=ui(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||aN.has(r)||n.push(r);return n}function iN(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(Ok(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Ok(n,"assistant",o))}}return t}function Ok(e,t,n){let r=Hn(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 di(e){let t=iN(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Ck(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,d]of l){let f=n.get(u);f||n.set(u,f=new Map),f.set(i,d)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function Cl(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(Ck(t))];if(!i.length)return[];let c=new Map;for(let u of i){let d=e.postings.get(u);if(!d)continue;let f=d.size,p=Math.log(1+(e.N-f+.5)/(f+.5));for(let[h,w]of d){let g=e.lengths[h],k=w+$k*(1-Rk+Rk*(g/(e.avgdl||1))),x=p*(w*($k+1)/k);c.set(h,(c.get(h)||0)+x)}}let l=[];for(let[u,d]of c){let f=e.chunks[u];o&&f.role!==o||s&&!f.tags.includes(s)||a&&f.boxId!==a||l.push({chunk:f,score:d})}return l.sort((u,d)=>d.score-u.score||d.chunk.seq-u.chunk.seq),l.slice(0,r)}var $k,Rk,aN,hp=U(()=>{Ol();$k=1.5,Rk=.75,aN=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 Ml from"node:fs/promises";import Mk from"node:path";async function cN(e){let t=Mk.join(e,".bostrat","events"),n;try{n=await Ml.readdir(t)}catch{return di([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Ml.readFile(Mk.join(t,o),"utf8");for(let a of s.split(`
|
|
122
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return di(r)}function lN(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 uN(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Cl(e,t,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),d=u.length>800?u.slice(0,800)+"\u2026":u,f=`- **${l}:** ${d}`;if(i+f.length>r||(a.push(f),i+=f.length,a.length>=n))break}return a.length?["",`## ${Nk}, 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 Pk(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Ml.readFile(t,"utf8");if(a.includes(Nk))return{appended:!1,reason:"already-enriched"};let i=await cN(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=uN(i,lN(a),{k:r,maxChars:o,exclude:s});return c?(await Ml.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 Nk,Dk=U(()=>{hp();Nk="Relevant prior discussion (verbatim, retrieved"});function wN(e){return dN.test(String(e||"").trimStart())}function Lk(e){let t=String(e||"");if(!wN(t.trimStart()))return null;let n=t.replace(fN,"").trim(),r=gN(n),o=[];for(let u of mN){let d=u.from.map(f=>Es(r.get(f))).find(f=>f&&!Bk(f));d&&o.push({h:u.h,text:fi(d,pN)})}let s=Es(r.get("Current Work"))||Es(r.get("Primary Request and Intent"))||bN(n),a=Pl(s),i=fi(Nl(yp(a[0]||"")),hN)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(fi(Nl(yp(l)),gp));for(let u of["Pending Tasks","Optional Next Step"]){let d=Pl(Es(r.get(u)))[0]||"";if(!(!d||Bk(d))){c.push(fi(`Left to do: ${Nl(yp(d))}`,gp));break}}if(!c.length){let u=Pl(Es(r.get("Problem Solving"))||"")[0];u&&c.push(fi(Nl(u),gp))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function gN(e){let t=new Map,n=[];wp.lastIndex=0;for(let r=wp.exec(e);r;r=wp.exec(e))n.push({name:yN(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 yN(e){return jk.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Es(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 bN(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Es(t.split(/\n{2,}/).slice(0,2).join(" "))}function Pl(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 yp(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 Nl(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Bk(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function fi(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Pl(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 dN,jk,wp,fN,pN,mN,hN,gp,Fk=U(()=>{dN=/^This session is being continued from a previous conversation that ran out of context\./,jk=["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"],wp=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${jk.join("|")})[:*\t ]*$`,"gim"),fN=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,pN=420,mN=[{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"]}],hN=116,gp=168});function Hk(e){return bp.has(String(e||"").replace(/^\//,"").toLowerCase())}function _s(e){let t=String(e||"").trim();if(!t)return null;let n=Lk(t);if(n)return n;let r=t.match(kN);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(xN);if(o)return{kind:"skill",name:o[1]};let s=t.match(vN);if(s){let i=s[1].toLowerCase();return bp.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(CN);if(a&&bp.has(a[1].toLowerCase()))return{kind:"suppress"};if(SN.test(t))return{kind:"suppress"};if(AN.test(t))return{kind:"suppress"};if(TN.test(t))return{kind:"suppress"};if(EN.test(t)){let i=l=>MN(t.match(l)?.[1].trim()||"")||null,c=i(ON);return{kind:"task",summary:i(_N)||"",taskId:i(IN),toolUseId:i($N),status:i(RN)?.toLowerCase()||null,resultHead:c?c.length>Uk?`${c.slice(0,Uk).trimEnd()}\u2026`:c:null}}return null}function MN(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Wk(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 qk(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var kN,xN,vN,SN,TN,AN,EN,_N,IN,$N,RN,ON,Uk,bp,CN,Dl=U(()=>{Fk();kN=/^Base directory for this skill:\s*(\S+)/,xN=/^Run the "([^"]+)" workflow\./,vN=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,SN=/^<local-command-(?:stdout|caveat)>/i,TN=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,AN=/^Continue from where you left off\.$/,EN=/^<task-notification>/i,_N=/<summary>([\s\S]*?)<\/summary>/i,IN=/<task-id>([\s\S]*?)<\/task-id>/i,$N=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,RN=/<status>([\s\S]*?)<\/status>/i,ON=/<result>([\s\S]*?)(?:<\/result>|$)/i,Uk=600,bp=new Set(["model","clear","compact","login","exit","quit"]);CN=/^\/([a-z0-9:_-]+)$/i});import NN from"node:crypto";function jN(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,Gk)){let o=pi(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,PN)){let c=pi(i?.label);if(!c)return null;let l=pi(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<DN)return null;n.push({question:o,header:pi(r?.header).slice(0,BN),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function kp(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Kk.lastIndex=0;let t=null;for(let o;o=Kk.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=jN(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function xp(e,t,n){let r=NN.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function vp(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Gk}={}){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||kp(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=xp(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 Vk(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=pi(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 Gk,PN,DN,BN,Kk,pi,zk,Sp=U(()=>{Gk=4,PN=4,DN=2,BN=24,Kk=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,pi=e=>typeof e=="string"?e.trim():"";zk=`
|
|
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 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
|
|
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 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:"2026-08-23",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). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; 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
|
|
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
|
|
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 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(
|
|
150
|
+
`});function LN(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 UN(e,t,n){let r=Yk(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 bn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,d=(b,m)=>{u.set(b,m),t.push(m)},f=b=>{let m=u.get(b);m&&(m.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,h=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let m=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let v of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let O=v[0].split("/").pop().toLowerCase();h.has(O)||h.set(O,[]),h.get(O).push(b.payload?.ts??b.ts)}}let w=3*6e4,g=20*6e4,k=(b,m)=>!!(b?.name&&h.get(String(b.name).toLowerCase()))?.some(O=>O>=m-w&&O<=m+g);for(let b of e||[])if(b.type==="turn.retracted"){r.add(`${b.boxId}|${b.payload?.saidTs}`);let m=Ve(b.payload?.text);m&&(o.has(b.boxId)||o.set(b.boxId,[]),o.get(b.boxId).push(m))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let m=Ve(b.payload?.text);m&&(s.has(b.boxId)||s.set(b.boxId,new Set),s.get(b.boxId).add(m))}else if(b.type==="job.started"&&b.payload?.id)a.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&c.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let m=b.payload.note,v={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:we(b)};m.toolUseId&&i.set(m.toolUseId,v);let O=String(m.summary||"").match(p);O&&l.set(O[1],v)}let x=new Map,y=new Map,_=null,M=new Map,j=new Map,L=new Map,F=new Map,A=new Map,I=new Map,C=new Map,J=new Map,q=new Map,R=new Map,W=new Map,X=new Map,G=new Map,Te=new Map,le=new Map,be=[],ae=b=>be.push(b),K=b=>{if(!be.length)return;let m=we(b);for(let v of be){if(v.status!=="open"||v.box&&v.box!==b.boxId||(v.ts??0)>m)continue;let O=t.indexOf(v);O>=0&&t.splice(O,1),v.ts=m,t.push(v)}},ue=new Map,re=new Map,V=new Map,$e=new Map,Me=new Map,fe=new Map,E=()=>{for(let b of fe.values())b.questions.length&&t.push(b);fe.clear()},P=null,he=6e4,Ze=new Map,Be=(b,m)=>{let v=o.get(b);if(!v?.length)return!1;let O=Ve(m),de=s.get(b);return v.some(ge=>{if(!O.startsWith(ge))return!1;let He=Ve(O.slice(ge.length));return!!He&&!!de?.has(He)})};for(let b of e||[]){let m=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"||(be.length=0,!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length))break;{let v=m.note||_s(m.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let O=String(v.summary||"").match(p);if(!v.toolUseId&&O&&c.has(O[1]))break}if(v.kind==="command"&&Hk(v.command))break;if(v.kind==="resume-summary"){t.push({id:b.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:we(b),box:b.boxId,agent:b.provider});break}v.kind!=="suppress"&&t.push({id:b.id,kind:"event",type:qk(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:we(b),box:b.boxId,agent:b.provider});break}}if(r.has(`${b.boxId}|${we(b)}`)||b.source==="transcript"&&Be(b.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){t.push({id:b.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:we(b),box:b.boxId,agent:b.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"&&(P=null);break}for(let v of M.values())v.status="stale";M.clear();for(let v of L.values())v.status="stale";L.clear();for(let v of G.values())v.status="stale";G.clear(),P&&P.box===b.boxId&&(t.push(LN(P)),P=null),t.push({id:b.id,kind:"message",role:"user",text:m.text,ts:we(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...b.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 v=m.from||{};t.push({id:b.id,kind:"event",type:"peer-message",text:m.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:we(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let v=nk(m,{codexFailedText:fo});if(v){t.push({id:b.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:b.provider,legacy:!0,at:we(b),ts:we(b),box:b.boxId,agent:b.provider});break}E();let O=(m.images||[]).filter(Ne=>!k(Ne,we(b)));if(!String(m.text||"").trim()&&!O.length)break;if(b.provider==="cursor"){let Ne=`${b.boxId}|${m.text}`;if(n.has(Ne))break;n.add(Ne)}(!P||P.box===b.boxId)&&(P=null);let de=b.provider==="codex"?(m.tools||[]).filter(Ne=>!Qf.has(Ne)&&!ti.has(Ne)):m.tools||[],ge=m.text,He=null,Lt=kp(String(m.text||""));Lt&&(ge=Lt.text,He={questions:Lt.questions}),t.push({id:b.id,kind:"message",role:"assistant",text:ge,ts:we(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...He?{ask:He}:{},...de.length?{tools:de}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...O.length?{images:O}:{}}),K(b),He&&(P={id:b.id,groupId:xp(b.provider,m.sourceId||b.id,He.questions),provider:b.provider,box:b.boxId,questions:He.questions,ts:we(b)});break}case"job.started":{if(!m.id)break;let v=we(b),O=i.get(m.id);if(!O&&(m.kind||"agent")==="agent"&&m.name){let Ne=l.get(m.name);Ne&&Ne.ts>=v&&(O=Ne)}let de={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:v,finishedTs:O?.ts??null,summary:O?.summary??null,resultHead:O?.resultHead??null},ge=Ze.get(b.boxId),He=ge?.jobs[ge.jobs.length-1]?.startedTs;if(ge&&He!=null&&v-He<=he)ge.jobs.push(de);else{let Ne={id:b.id,kind:"job-dock",dockId:m.id,ts:v,box:b.boxId,agent:b.provider,jobs:[de]};Ze.set(b.boxId,Ne),t.push(Ne)}break}case"handoff.done":t.push(ft(b,"lead-handoff"));break;case"subtask.delegated":t.push({...ft(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":t.push(ft(b,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let O of M.values())O.status="stale";M.clear();for(let O of L.values())O.status="stale";L.clear()}let v=ft(b,`pr-${m.change}`);if(v.box=b.boxId??null,FN.has(m.change)&&m.number!=null){let O=UN(x,y,m);if(x.size>=2){for(let ge of y.values())for(let He of ge)He.grouped=!0;v.groupPrs=[...x.values()];let de=[v];for(let ge of x.keys())y.set(ge,de)}else{let de=y.get(O)??[];de.push(v),y.set(O,de)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let O=Yk(m),de=_&&!_.complete||x.size>=2;if(x.has(O))x.delete(O),y.delete(O);else if(m.number!=null)for(let ge of[...x.keys()])ge.endsWith(`#${m.number}`)&&(x.delete(ge),y.delete(ge));if(de&&m.number!=null){(!_||_.complete)&&(_={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:we(b),ts:we(b),box:b.boxId,agent:b.provider},t.push(_)),_.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:we(b)}),_.remaining=[...x.values()],_.complete=x.size===0;break}}t.push(v);break}case"pr.offered":{if(!m.offerId)break;for(let O of M.values())O.status="stale";M.clear();for(let O of L.values())O.status="stale";L.clear();let v={id:b.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:b.source==="cli"||m.via==="cli"?"cli":"transcript",ts:we(b),box:b.boxId,agent:b.provider};M.set(m.offerId,v),j.set(m.offerId,v),t.push(v),v.source==="cli"&&ae(v);break}case"pr.offer.answered":{let v=j.get(m.offerId);v&&(v.status=m.action||"dismiss",v.actions=Array.isArray(m.actions)?m.actions:null,v.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(v.existingPr={number:m.existingPr.number??null}),M.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let O of L.values())O.status="stale";L.clear();let v={id:b.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:we(b),box:b.boxId,agent:b.provider};L.set(m.offerId,v),F.set(m.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=F.get(m.offerId);v&&(v.status=m.choice||"dismiss",L.delete(m.offerId));break}case"box.state.changed":{let v=m.change==="model"?"model-change":m.change;if(!v)break;let O=ft(b,v);if(v==="model-change"&&(O.to=m.model),v==="node-update"&&O.latest){let de=t.findIndex(ge=>ge.kind==="event"&&ge.type==="node-update"&&ge.latest===O.latest);if(de>=0){let ge=t[de];t[de]={...O,ts:ge.ts,at:ge.at,firstAt:ge.firstAt??ge.ts,updatedAt:O.ts};break}}t.push(O);break}case"track.transferred":case"track.recovered":t.push(ft(b,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){f(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;d(`view:${m.viewId}`,{id:b.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:we(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{t.push({id:b.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:we(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(m.change==="picked"){t.push({id:b.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:we(b),box:b.boxId,agent:b.provider});break}if(m.change==="deleted"){f(`mock:${m.mockId}`);break}if(m.change!=="created")break;d(`mock:${m.mockId}`,{id:b.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:we(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(m.change==="deleted"){f(`flow:${m.flowId}`);break}if(m.change!=="created")break;d(`flow:${m.flowId}`,{id:b.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:we(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){f(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;d(`runbook:${m.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:we(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{t.push({id:b.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:we(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let O of C.values())O.box===(m.box||b.boxId)&&(O.status="stale",C.delete(O.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:we(b),box:m.box||b.boxId,agent:m.agent||b.provider};C.set(m.offerId,v),J.set(m.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=J.get(m.offerId);v&&(v.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",C.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 v={id:b.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:we(b),box:b.boxId,agent:b.provider};A.set(m.offerId,v),I.set(m.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=I.get(m.offerId);v&&(v.status=m.action==="moved"?"moved":"dismiss",A.delete(m.offerId));break}case"ticket.state.changed":{for(let v of A.values())v.key===m.key&&v.to===m.to&&(v.status="stale",A.delete(v.offerId));if(m.mode==="offer")break;t.push(ft(b,"ticket-state"));break}case"ticket.commented":{t.push({id:b.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:we(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":t.push(ft(b,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let O of q.values())O.status="stale";q.clear();let v={id:b.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:we(b),box:b.boxId,agent:b.provider};q.set(m.proposalId,v),R.set(m.proposalId,v),t.push(v),ae(v);break}case"ticket.create.answered":{let v=R.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",v.createdCount=m.createdCount??null,v.failedCount=m.failedCount??null,q.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let O of W.values())O.status="stale";W.clear();let v={id:b.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:we(b),box:b.boxId,agent:b.provider};W.set(m.proposalId,v),X.set(m.proposalId,v),t.push(v),ae(v);break}case"loop.create.answered":{let v=X.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",W.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let v=m.replaces?le.get(m.replaces):null;v&&v.status==="armed"&&(v.status="replaced");let O={id:b.id,kind:"followup-armed",armId:m.armId,trigger:m.trigger??"now",label:m.label??"",prompt:m.prompt??"",why:m.why??null,status:"armed",ts:we(b),box:b.boxId,agent:b.provider};le.set(m.armId,O),O.trigger!=="now"&&t.push(O);break}case"followup.armed.cancelled":{let v=le.get(m.armId);v&&v.status==="armed"&&(v.status="cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let de of G.values())de.trigger===m.trigger&&(de.status="stale",G.delete(de.offerId));let v=Array.isArray(m.rows)?m.rows:[];for(let de of v){let ge=de.armId?le.get(de.armId):null;ge&&ge.status==="armed"&&(ge.status="offered")}let O={id:b.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:v.map(de=>({followupId:de.followupId??null,armId:de.armId??null,label:de.label??"",prompt:de.prompt??null,promptPreview:de.promptPreview??null,promptHash:de.promptHash??null,origin:de.origin??"operator",why:de.why??null})),status:"open",answer:null,ts:we(b),box:b.boxId,agent:b.provider};G.set(m.offerId,O),Te.set(m.offerId,O),t.push(O),O.trigger==="now"&&ae(O);break}case"followup.answered":{let v=Te.get(m.offerId);v&&(v.status=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss",v.answer={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0},G.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let O of ue.values())O.status="stale";ue.clear();let v={id:b.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:we(b),box:b.boxId,agent:b.provider};ue.set(m.proposalId,v),re.set(m.proposalId,v),t.push(v),ae(v);break}case"followup.create.answered":{let v=re.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",ue.delete(m.proposalId));break}case"followup.created":{let v=m.proposalId?re.get(m.proposalId):null;v&&(v.created={followupId:m.followupId??null});break}case"loop.created":{let v=m.proposalId?X.get(m.proposalId):null;v&&(v.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(ft(b,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let de of V.values())de.loopId===m.loopId&&(de.status="stale",de.staleReason="superseded",V.delete(de.proposalId));let v=b.type==="loop.update.offered",O={id:b.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...v?{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:we(b),box:b.boxId,agent:b.provider};V.set(m.proposalId,O),$e.set(m.proposalId,O),t.push(O),ae(O);break}case"loop.update.answered":{let v=$e.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",V.delete(m.proposalId));break}case"loop.delete.answered":{let v=$e.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",V.delete(m.proposalId));break}case"loop.updated":{let v=m.proposalId?$e.get(m.proposalId):null;v&&(v.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(ft(b,"loop-updated"));break}case"loop.deleted":t.push(ft(b,"loop-deleted"));break;case"loop.paused":t.push(ft(b,"loop-paused"));break;case"rework.cause.offered":{if(!m.sha||Me.has(m.sha))break;let v={id:b.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:we(b),box:b.boxId,agent:b.provider};Me.set(m.sha,v),t.push(v);break}case"rework.cause.answered":{let v=m.sha?Me.get(m.sha):null;v&&(v.status="answered",v.cause=m.cause??null);break}case"rework.cause.retracted":{let v=m.sha?Me.get(m.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={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=v;break}t.push({id:b.id,kind:"ticket-created",...v,ts:we(b),box:b.boxId,agent:b.provider});break}case"error":{if(!m.kind)break;t.push(ft(b,m.kind));break}case"provider.error":t.push({...ft(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":t.push(ft(b,"usage-cap-hit"));break;case"usage.cap.warning":t.push(ft(b,"usage-cap-warning"));break;case"provider.connected":{t.push({id:b.id,kind:"event",type:"provider-connected",provider:m.provider||b.provider,ts:we(b),at:we(b),box:b.boxId,agent:b.provider});break}case"prompt.answered":{if(P=null,m.control||!m.question)break;let v=m.groupId||m.promptId;if(!v)break;let O=fe.get(v);O||(O={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:we(b)},fe.set(v,O));let de=m.questionIndex??0,ge={index:de,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},He=O.questions.findIndex(Ne=>Ne.index===de&&Ve(Ne.question)===Ve(ge.question));He>=0?O.questions[He]=ge:O.questions.push(ge),O.questions.sort((Ne,Xt)=>Ne.index-Xt.index),O.questionTotal=Math.max(O.questionTotal,m.questionTotal??1),O.questions.length>=O.questionTotal&&(t.push(O),fe.delete(v));break}default:break}}let Et=u.size?t.filter(b=>!b.retired):t;return WN(Et.sort((b,m)=>(b.ts??0)-(m.ts??0)))}function WN(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<=HN){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 Tp(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:qN(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??bn(e)).filter(o=>o.kind==="message");return zb(n,r)}function qN(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==xs(e.command)?t:vr(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:we(e),ts:we(e)}}function Jk(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)+KN,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&GN.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Xk(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!zN.has(r.type)||!r.box)continue;let o=(r.ts??0)+VN,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 Bl(e){return(e||[]).filter(t=>YN.has(t?.kind)&&t.status==="open")}var we,FN,Yk,HN,KN,GN,VN,zN,YN,jl=U(()=>{vs();po();vl();Rl();Dl();Sp();we=e=>e.payload?.ts??e.ts;FN=new Set(["opened","ci-fixed","reconciled","pushed"]),Yk=e=>`${e.repo??""}#${e.number}`;HN=2*6e4;KN=12e4,GN=new Set(["archived","unarchived","opened"]);VN=2500,zN=new Set(["pr-opened","pr-merged","pr-auto-armed"]);YN=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 eP(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 nP(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 rP(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<ZN&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<QN&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function t0(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=eP(e),s=nP(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=bn(e??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-JN);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(e0[r]??e0.failover),l.push(""),o.lastPush?.sha){let f=Qk(n,o.lastPush.ts),p=f?` (~${f} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let f=Qk(n,i);l.push(`## Last real checkpoint (written by the previous agent${f?`, ~${f} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=rP(e??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(f=>`\`${f}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(f=>`\`${f}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let f of c)l.push(`**${f.role==="user"?"operator":"agent"}:** ${tP(f.text,XN)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let d=l.join(`
|
|
151
|
+
`);return d.length<=Zk?d:`${d.slice(0,Zk)}
|
|
152
|
+
|
|
153
|
+
[\u2026 brief truncated]`}var JN,XN,Zk,ZN,QN,tP,Qk,e0,n0=U(()=>{jl();JN=12,XN=2e3,Zk=48e3,ZN=30,QN=10;tP=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},Qk=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;e0={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 oP from"node:fs";import Tr from"node:fs/promises";import Ar from"node:path";function u0(e){let t=io(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=r0?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,r0).toString("utf8").replace(/�+$/,"")}
|
|
154
|
+
|
|
155
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function pP(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(s0.test(s)){if(r.length>=w0)continue;let a=fP(s.replace(s0,"").replace(/[.;,:]+$/,""),g0,uP);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>o0&&(o=`${o.slice(0,o0-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function y0(e,t,{timeoutMs:n=9e4}={}){let r=Ar.join(t,cP),o=Ar.join(r,lP);await Tr.mkdir(r,{recursive:!0});let s=await Ll(o),a=await xe(e);if(!a)throw new Error(`session "${e}" not found`);return await Fl(e,a,dP(o)),await A0(o,s,n),pP(await Tr.readFile(o,"utf8"))}function b0(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===f0||n===h0;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(d0)||n===sP||r.startsWith(p0)||r.startsWith(m0)}function k0(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-mP;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 x0(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 v0(e,{windows:t=[]}={}){if(b0(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 Ul(e){return Ap.get(e)||null}async function S0(e){let t=Ap.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await xe(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:Ar.join(n.cwd,c0,l0)};return Ap.set(e,r),gP(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function gP(e,t){e.step="summarize",await T0(e.name,t),e.step="clear";let n=await xe(e.name);await Fl(e.name,n,"/clear"),await Ip(hP),e.step="resume",await Fl(e.name,n,iP),e.step="done",e.finishedAt=Date.now()}async function T0(e,t,{timeoutMs:n=wP}={}){let r=Ar.join(t,c0),o=Ar.join(r,l0);await Tr.mkdir(r,{recursive:!0});let s=await Ll(o),a=await xe(e);await ce({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Fl(e,a,aP(o)),await A0(o,s,n),await ce({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Ll(o)});let i=await Pk(t,o,{exclude:u=>b0(u.role,u.text)});await ce({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 Tr.readFile(o,"utf8");await $({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...u0(u),ts:Date.now()}})}catch{}}await _f(t);let l=await yP(r,o);return c&&await wb(c,l||o,{box:e,agent:a?.agent??null}),o}async function Ep(e,{timeoutMs:t}={}){let n=await xe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return T0(e,n.cwd,t?{timeoutMs:t}:void 0)}async function _p(e,t,{trackName:n="this Track"}={}){let r=await H({id:e,cwd:t}),o=t0(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Ar.join(t,a0);return await Tr.mkdir(Ar.dirname(s),{recursive:!0}),await Tr.writeFile(s,o,"utf8"),await $({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...u0(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),a0}async function Ll(e){try{return(await Tr.stat(e)).mtimeMs}catch{return 0}}async function A0(e,t,n){let r=Date.now()+n;for(;;){if(await Ll(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Ip(2e3)}}async function yP(e,t){try{if(!oP.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Ar.join(e,`${n}.md`);return await Tr.copyFile(t,r),r}catch{return null}}function Ip(e){return new Promise(t=>setTimeout(t,e))}function i0(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 Fl(e,t,n){if(t){let o=i0(t.agent);await Sr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await xe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await We(e,["Escape"],{kind:"drive-wipe"}),await Ip(150),await gs(e,bl,{kind:"drive-wipe"}),await nt(e,n,{kind:"orchestration-drive",...yn(r.agent,n)}),await li({sessionName:e,agent:r.agent,agentCmd:i0(r.agent),text:n,flow:"orchestration-drive"})}var c0,l0,r0,d0,f0,p0,sP,aP,iP,m0,h0,cP,lP,o0,w0,g0,uP,dP,s0,fP,mP,hP,wP,Ap,a0,$p=U(()=>{ut();si();wn();ai();mp();Dk();ja();me();ul();n0();c0=".bostrat/handoff",l0="latest.md",r0=32*1024;d0="Checkpoint: write a work-in-progress handoff",f0="HANDOFF WRITTEN",p0="Resume from .bostrat/handoff/latest.md",sP="/clear",aP=e=>`${d0} 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 ${f0} and take no further action.`,iP=`${p0}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,m0="Beat: write a",h0="BEAT WRITTEN",cP=".bostrat/arc",lP="beat.txt",o0=200,w0=4,g0=5,uP=36,dP=e=>`${m0} 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 ${w0} further lines, each starting with "- " and naming ONE piece in at most ${g0} 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 ${h0} and take no further action.`,s0=/^(?:[-–—*•·]|\d+[.)])\s+/,fP=(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};mP=6e5;hP=3500,wP=3e5,Ap=new Map;a0=".bostrat/handoff/status-latest.md"});function _r(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Hl.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 I0(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:_r(o.from,"from",n),toMs:_r(o.to,"to",n)}))}return t}function Op(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 kP(e){if(!e)return null;let t=String(e).trim();return Er[t]?t:E0[t]?E0[t]:Object.keys(Er).find(r=>t.startsWith(r))||null}function xP(e){if(!e||e===Er)return bP;let t=_0.get(e);return t||(t=I0(e),_0.set(e,t)),t}function $0(e,t,n={}){let{table:r,speed:o}=n,s=xP(r),a=r&&r!==Er?s[e]?e:null:kP(e);if(!a)return null;let i=Op(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 kn(e,t={},n,r){let o=$0(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 vP(e=Er){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:_r(a.from,"from",n),toMs:_r(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`),Hl.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 TP({now:e=Date.now(),table:t=Er,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...SP,...n},a=[],i=Op(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(d=>({...d,fromMs:_r(d.from,"from",c),toMs:_r(d.to,"to",c),verifiedMs:Hl.test(String(d.verifiedAt??""))?Date.parse(`${d.verifiedAt}T00:00:00.000Z`):NaN}))}catch(d){a.push(String(d.message));continue}for(let d of u){if(!Number.isFinite(d.verifiedMs)){a.push(`${c}: window ${d.from??"\u2026"} \u2192 ${d.to??"now"} has no verifiedAt`);continue}let f=i>=d.fromMs&&i<=d.toMs,p=d.fromMs>i&&d.fromMs-i<=o*Rp;f&&i-d.verifiedMs>r*Rp&&a.push(`${c}: current rate last verified ${d.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),p&&d.fromMs-d.verifiedMs>s*Rp&&a.push(`${c}: scheduled rate change on ${d.from} was last verified ${d.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function Is({now:e=Date.now(),table:t=Er,...n}={}){let r=Op(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=_r(l.from,"from",i),d=_r(l.to,"to",i);r>=u&&r<=d&&Hl.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=[...vP(t),...TP({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Er,E0,Hl,bP,_0,R0,SP,Rp,Wn=U(()=>{Er={"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:"2026-08-23",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). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; 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)."}]},E0={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"},Hl=/^\d{4}-\d{2}-\d{2}$/;bP=I0(Er);_0=new WeakMap;R0=(e,t,n)=>$0(e,t,n)!==null;SP=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Rp=864e5});function AP(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function C0(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=AP(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=kn(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&&!R0(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function Ir(e,{boxLabels:t={}}={}){let n=(e||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=O0();for(let f of n){let{usd:p,model:h,tokens:w,repriced:g,unpriced:k,asRecorded:x}=Ut(f);i+=x;let y=f.provider||"unknown",_=f.boxId||"unattributed";if(g&&c++,k&&(l++,u.add(h)),a+=p,C0(d,w),r[h]||={usd:0,tokens:O0(),events:0},r[h].usd+=p,r[h].events+=1,C0(r[h].tokens,w),o[y]||={usd:0,events:0},o[y].usd+=p,o[y].events+=1,s[_]||={usd:0,events:0,provider:y,models:new Set,label:t[_]||_},s[_].usd+=p,s[_].events+=1,h!=="unknown"&&s[_].models.add(h),f.epoch!=null){let M=(s[_].epochs||={})[f.epoch]||={usd:0,events:0,provider:y,models:new Set};M.usd+=p,M.events+=1,M.provider=y,h!=="unknown"&&M.models.add(h)}}return{totalUsd:$s(a),asRecordedUsd:$s(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Wl(r,f=>({...f,usd:$s(f.usd)})),byProvider:Wl(o,f=>({...f,usd:$s(f.usd)})),byBox:Wl(s,f=>({usd:$s(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:Wl(f.epochs,p=>({usd:$s(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var O0,$s,Wl,$r=U(()=>{Wn();O0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});$s=e=>Math.round(e*1e4)/1e4,Wl=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function EP(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 M0(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let d=u.payload||{};!d.repo||d.number==null||(d.change==="merged"?o.set(`${d.repo}#${d.number}`,d):d.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(d=>{let[f,p]=d.split("#");return`${f.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Cp(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Cp(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(d=>`${d.fromAgent}\u2192${d.toAgent}`).join(", ");n.push(`${Cp(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Ir(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?EP(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Cp,N0=U(()=>{$r();Cp=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as _P}from"node:child_process";import{promisify as IP}from"node:util";import{randomUUID as $P}from"node:crypto";async function Mp(e,t){let n=Date.now()+t;for(;;){if(await Fn(e))return!0;if(Date.now()>n)return!1;await mi(OP)}}async function MP(e,t){let n=D0[t]||D0.default;n.escape&&(await We(e,["Escape"],{kind:"swap-exit"}),await mi(150)),n.wipe&&await gs(e,bl,{kind:"swap-exit"}),await kl(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=yn(t).submitDelayMs||0;await mi(Math.max(r,250)),await We(e,["Enter"],{kind:"swap-exit"})}async function B0(e,t){try{let{stdout:n}=await ho(Y,Z("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function j0(e){await ho(Y,Z("respawn-pane","-k","-t",e))}async function Np(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 xe(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||br(n),a=Number(t.exitTimeoutMs)||RP;await ce({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Fn(e)&&(await MP(e,o),!await Mp(e,a)&&(i=!0,await ce({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await j0(e),!await Mp(e,P0))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&co(n),l=c?Kc():null,u=await B0(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await ho(Y,Z("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await ho(Y,Z("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?pl(g,l):g}if(d&&!i&&(await j0(e),!await Mp(e,P0)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,p=t.resumeSessionId||$P(),h=f?n:za(n,p,e),w=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await fr(r.cwd),s==="codex"&&await os(r.cwd),s==="cursor"&&await ss(r.cwd),await lo(e),await ho(Y,Z("set-option","-t",e,"@bostrat-sid",p)),await ho(Y,Z("set-option","-t",e,"@bostrat-agent",s)),await mi(CP),c&&Va().length&&(await B0(e,Ga)||await ho(Y,Z("set-environment","-t",e,Ga,"1")).catch(()=>{}),await nt(e,`export ${Ga}=1`,{kind:"swap-env"}),await mi(150)),await nt(e,h,{kind:"swap-launch"}),await ce({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:d,launchCmd:h}}var ho,mi,RP,P0,OP,CP,D0,L0=U(()=>{ut();Oa();Vc();zc();ai();wn();ho=IP(_P),mi=e=>new Promise(t=>setTimeout(t,e)),RP=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,P0=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,OP=250,CP=400,D0={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 F0(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Tt(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 d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let p=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let p=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(p?xs(p):null),detail:p?xs(p):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&NP.test(PP(f.content))&&s.add(f.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var NP,PP,U0=U(()=>{nn();vs();NP=/^Async (?:agent|task) launched/i,PP=e=>{if(typeof e=="string")return e.trim();if(!Array.isArray(e))return"";let t=e.find(n=>n&&n.type==="text"&&typeof n.text=="string");return t?t.text.trim():""}});function Rr(e,t){if(!e)return null;let n=String(e);if(t){let o=t.endsWith("/")?t:`${t}/`;if(n===t)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}function DP(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 H0(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:rn(`read ${Rr(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Rs(r)}`:`Read ${Rs(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Rr(t.path,n):null,s=t?.glob?` --glob ${Pp(t.glob)}`:"",a=rn(String(r),40);return{command:rn(`grep ${Pp(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=t?.glob_pattern;if(!r)return null;let o=t?.target_directory?Rr(t.target_directory,n):null;return{command:rn(`glob ${Pp(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${rn(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:rn(`edit ${Rr(t.path,n)}`,140),description:`Edit ${Rs(t.path)}`}:null;case"Write":return t?.path?{command:rn(`write ${Rr(t.path,n)}`,140),description:`Write ${Rs(t.path)}`}:null;case"Delete":return t?.path?{command:rn(`delete ${Rr(t.path,n)}`,140),description:`Delete ${Rs(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Rr(s,n)).filter(Boolean);return{command:rn(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=DP(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:rn(`apply_patch ${Rr(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Rs(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:rn(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var ql,rn,Rs,Pp,W0=U(()=>{ql=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),rn=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,Rs=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);Pp=e=>`"${String(e).replace(/"/g,'\\"')}"`});import hi from"node:fs";import BP from"node:fs/promises";import wo from"node:path";import jP from"node:os";import LP from"node:crypto";import FP from"node:zlib";function G0(e){let t=LP.createHash("md5").update(String(e)).digest("hex");return wo.join(UP,t)}function V0(e){try{let t=JSON.parse(hi.readFileSync(wo.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 HP(e){let t=G0(e),n;try{n=hi.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=wo.join(t,o.name),a=wo.join(s,"store.db");if(!hi.existsSync(a))continue;let i=V0(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 Kl(e,t){if(!e)return null;if(t){let n=wo.join(G0(e),t),r=wo.join(n,"store.db");if(hi.existsSync(r))return{id:t,dir:n,store:r,...V0(n)||{createdAtMs:null,updatedAtMs:null}}}return HP(e)}function q0(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function WP(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=FP.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function qP(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 KP(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!ql.has(r.toolName))continue;let o=H0(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 Dp(e,t){if(!K0)return Os;let{DatabaseSync:n}=K0,r;try{r=new n(e,{readOnly:!0})}catch{return Os}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 Os;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Os}let a=s.lastUsedModel||null,i=s.latestRootBlobId?q0(r,s.latestRootBlobId):null;if(!i)return{...Os,model:a};let c=[],l=[],u=[],d=null,f=[];for(let p of qP(i)){let h=WP(q0(r,p));if(!h)continue;let w=h.providerOptions?.cursor;if(w?.requestId&&f.push(w.requestId),w?.modelProviderMessageId&&f.push(w.modelProviderMessageId),h.role!=="assistant")continue;if(Array.isArray(h.content))for(let x of h.content){let y=x?.providerOptions?.cursor?.modelName;y&&(d=y)}l.push(...KP(h.content,t).map((x,y)=>({...x,id:x.id||`${p}:${y}`}))),u.push(...VP(h.content));let g=GP(h.content);if(!g)continue;let k=[...new Set(u)];u=[],c.push({id:p,text:g,tools:k.length?k:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return Os}finally{try{r.close()}catch{}}}function z0(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(hi.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 Y0(e,t,{limit:n=80}={}){let r=Kl(e,t);if(!r)return[];let{turns:o,model:s}=await Dp(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=z0(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 J0(e,t){let n=Kl(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Dp(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 X0(e,t,{limit:n=80}={}){let r=Kl(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await Dp(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=z0(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:vr(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:vr(u.command,u.description),ts:i+1});return l}async function Z0(e,t){let n=Kl(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await BP.stat(n.store)).mtimeMs)}catch{return null}}var UP,GP,VP,K0,Os,Bp=U(()=>{vs();W0();UP=process.env.BOSTRAT_CURSOR_CHATS||wo.join(jP.homedir(),".cursor","chats");GP=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():"",VP=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],K0=process.getBuiltinModule?.("node:sqlite"),Os={turns:[],trailingCommands:[],model:null,requestIds:[]}});import zP from"node:fs/promises";function Up(e){return Q0.set(e.agent,e),e}function wi(e){return Q0.get(e)||YP}async function Wp(e,t={}){return(await wi(e.agent).readMessages(e,t)).map(Hp(e))}async function ex(e,t={}){return(await wi(e.agent).readCommands(e,t)).map(Hp(e))}async function tx(e,t={}){return(await wi(e.agent).readJobs(e,t)).map(Hp(e))}var Cs,jp,Lp,Fp,YP,Q0,Hp,nx=U(()=>{nn();Sl();vs();U0();Bp();po();Cs=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}},jp=class extends Cs{transcript(t){return!t?.cwd||!t?.sessionId?null:tn(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?tp(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?Vb(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?F0(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await zP.stat(n)).mtimeMs)}catch{return null}}},Lp=class extends Cs{async readMessages(t,n){return Y0(t.cwd,t.sessionId,n)}async readCommands(t,n){return X0(t.cwd,t.sessionId,n)}async updatedAt(t){return Z0(t.cwd,t.sessionId)}},Fp=class extends Cs{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Xb(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Zb(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Qb(t.cwd,this.sessionId(t))}},YP=new Cs("unknown"),Q0=new Map;Up(new jp("claude",Jf));Up(new Lp("cursor",ql));Up(new Fp("codex",ti));Hp=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function bo(e){let t=e.search(Vp);return(t>=0?e.slice(0,t):e).trim()}function JP(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function XP(e){let t=String(e).trim();return!!(!t||yo.test(e)||Vp.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function ZP(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=QP(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function QP(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(f=>f.length>3&&n.includes(f)).length;if(u)return u;let d=l.slice(0,8).trim();return d.length>=4&&n.includes(d)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function ox(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function eD(e,t=""){let n=String(e);if(!ax.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 d=r[u].match(go);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!yo.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:bo(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 nD(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||!ix.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(go);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&&!(tD.test(n[c])||yo.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:bo(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 rD(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(go);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 sD(e,t=""){let n=String(e);if(!qp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
161
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(oD.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let d=r[u].match(go);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!yo.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>qp.test(u.label)))return null;let i=s.map(u=>({label:bo(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 zp(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(go);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 aD(e,t=""){let n=String(e);if(!cx.test(n)||!lx.test(n)||ko.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=zp(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:bo(s.label),description:""}))}]}:null}function iD(e,t=""){let n=String(e);if(!ux.test(n)||ko.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=zp(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:bo(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 lD(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!cD.test(o)||Yp.test(e[r]))break;n+=o}return n}function uD(e,t=""){let n=String(e);if(!Yp.test(n)||ko.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=lD(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 fD(e,t=""){let n=String(e);if(!Kp.test(n)||!dD.test(n)||ko.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
165
|
+
`)].reverse().find(a=>Kp.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 pD(e,t=""){let n=String(e);if(!dx.test(n)||ko.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o=zp(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:bo(s.label),description:""}))}]}:null}function mD(e,t=""){let n=String(e);if(ko.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(fx);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(yo.test(r[l])||Vp.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 hD(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(Gp.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 wD(e,t=""){let n=String(e);if(!sx.test(n)||!Gp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
|
+
`),o=hD(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let F=i;F<c;F++){let A=r[F].match(go);A&&(u<0&&(u=F),l.push({line:F,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 F of l)for(let A=F.line+1;A<Math.min(r.length,F.line+4)&&!(go.test(r[A])||yo.test(r[A]));A++){let I=r[A].trim();if(!(!I||XP(r[A])||rx.test(I))){F.desc=JP(I);break}}let d=l.filter(F=>!rx.test(F.label));if(!d.length)return null;let f=d.some(F=>F.hasCheckbox),p=Math.max(0,d.findIndex(F=>F.cursor)),h=s>=0?s+1:0,w=[];for(let F=h;F<u;F++){let A=r[F].trim();A&&!yo.test(r[F])&&!/[☐☒]/.test(r[F])&&w.push(A)}let g=w.join(" ")||(s>=0?ox(r[s]):"")||"The agent is asking a question",k=s>=0?ZP(r[s],g):null,x=k?.currentTabLabel||(s>=0?ox(r[s]):""),y=d.map(F=>({label:bo(F.label),description:F.desc})),_=y.map(F=>F.label).join("|"),j=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${x}:${g}:${_}`.slice(0,240),L=k?`claude:ask:${k.tabs.map(F=>F.label).join("|")}`.slice(0,240):j;return{provider:"claude",box:t,multiSelect:f,cursorIndex:p,promptId:j,groupId:L,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:g,header:x,multiSelect:f,options:y}]}}function xo(e,t){let n=String(t||"");return n?e==="claude"?sx.test(n)&&Gp.test(n)||ax.test(n)||qp.test(n)||ix.test(n.trimEnd().split(`
|
|
169
|
+
`).pop()||"")?!0:ko.test(n)?!1:cx.test(n)&&lx.test(n)||ux.test(n)||Yp.test(n)||Kp.test(n)||dx.test(n)||fx.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 Or(e,t){return!e||!t?null:e.agent==="claude"?rD(t,e.name)||wD(t,e.name)||eD(t,e.name)||sD(t,e.name)||fD(t,e.name)||uD(t,e.name)||iD(t,e.name)||aD(t,e.name)||pD(t,e.name)||mD(t,e.name)||nD(t,e.name):e.agent==="cursor"||e.agent==="codex"?mk(e.agent,t,e.name):null}async function Jp(e){if(!e)return null;let t=await Un(e.name).catch(()=>"");return Or(e,t)}function px(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 mx(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 sx,Gp,go,rx,yo,Vp,ax,ix,tD,qp,oD,ko,cx,lx,ux,Yp,cD,Kp,dD,dx,fx,Xp=U(()=>{ut();Al();sx=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Gp=/Chat about this/i,go=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,rx=/^(type something|chat about this|submit)\b/i,yo=/^[─—-]{6,}\s*$/,Vp=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;ax=/^\s*Switch model\?\s*$/m;ix=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,tD=/^\s*[─—▔-]{6,}\s*$/;qp=/Resume from summary/i,oD=/^\s*This session is\b/;ko=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;cx=/Choose the text style that looks best with your terminal/,lx=/Let's get started\./;ux=/Select login method:/;Yp=/Paste code here if prompted/,cD=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;Kp=/OAuth error:/,dD=/Press Enter to retry\./;dx=/Quick safety check: Is this a project you created or one you trust/;fx=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Gl(e,t,n){if(!e||!t)return null;for(let r of gD)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function bD(e){let t=Zp.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},Zp.set(e,t)),t}function gx(e,t,n=Date.now()){let r=bD(e);if(r.sentAt=r.sentAt.filter(s=>n-s<wx),r.sentAt.length>=hx)return{action:"give-up"};if(n-r.last<yD)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 yx(e,t=Date.now()){let n=Zp.get(e);return n?n.sentAt.filter(r=>t-r<wx).length>=hx:!1}var gD,yD,hx,wx,Zp,Qp=U(()=>{gD=[{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"]}];yD=5e3,hx=5,wx=3600*1e3,Zp=new Map});function Ms(e){return typeof e=="string"?e.trim():""}var bx=U(()=>{});import{execFile as kD}from"node:child_process";import{promisify as xD}from"node:util";async function Ke(e,t,{input:n}={}){let r=tm("git",["-C",e,...t],{maxBuffer:kx});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Ns(e){if(!e)return!1;try{let{stdout:t}=await Ke(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function xx(e){let{stdout:t}=await Ke(e,["rev-parse","--show-toplevel"]);return t.trim()}async function vD(e){try{return await Ke(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Ps(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Ke(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>tm("git",["-C",e,...a],{maxBuffer:kx,env:o,...i});try{let a=await vD(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 tm("rm",["-f",r])}catch{}}}async function nm(e,t,{trailers:n=[]}={}){let{stdout:r}=await Ke(e,["status","--porcelain"]);return r.trim()?(await Ke(e,["add","-A"]),await Ke(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function gi(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Ke(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 yi(e,t){if(!t)return 0;try{let{stdout:n}=await Ke(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Ds(e,t,n,r){return await Ke(e,["worktree","add","--no-track","-b",n,t,r]),t}async function vx(e,t){try{return await Ke(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function Vl(e,t,n){return await Ke(e,["worktree","add","--end-of-options",t,n]),t}async function Bs(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Ke(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 Ke(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function rm(e){await Ke(e,["init","-q"]),await Ke(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 Ke(e,["worktree","remove","--force",t])}catch{try{await Ke(e,["worktree","prune"])}catch{}}}function SD(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 om(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function sm(e,t,n,r){let{stdout:o}=await Ke(e,om(["diff","--shortstat",t,n],r));return SD(o)}async function am(e,t,n,r){let{stdout:o}=await Ke(e,om(["diff","--name-only",t,n],r));return o.split(`
|
|
173
|
+
`).map(s=>s.trim()).filter(Boolean)}async function im(e,t,n,r){let{stdout:o}=await Ke(e,om(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await em(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await em(e,o,["--3way","--whitespace=nowarn"]),a=await TD(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await em(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function em(e,t,n){try{return await Ke(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function TD(e){try{let{stdout:t}=await Ke(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
174
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var tm,kx,zl=U(()=>{tm=xD(kD),kx=64*1024*1024});function Yl(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!AD.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,AD,xn=U(()=>{Ge=e=>!!(e&&(e.archivedAt||e.transferredTo)),AD=new Set(["merged","conflict","canceled","failed","handed-off"])});import cm from"node:fs/promises";import Tx from"node:path";import ED from"node:os";function Ix(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function $x(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function Rx(e){let t=new Date(Ix(e));return t.setDate(t.getDate()+7),t.getTime()}function Ox(e){let t=new Date($x(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Xl(e=Date.now()){for(let[t,n]of Object.entries(js)){let r=n.startOf(e);if(r!==qn[t]){qn[t]=r;for(let o of vo.values())o[t]=0;for(let o of Ls)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Ls.delete(o)}}}function Cx(e,t){let n=on?.caps?.[e]?.[js[t].capKey];return Number.isFinite(n)&&n>0?n:null}function So(e,t=Date.now()){if(!e||!um||!on?.caps)return null;Xl(t);let n=vo.get(e);for(let r of Object.keys(js)){let o=Cx(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Jl(s),resetsAt:js[r].nextStart(t)}}return null}function Mx(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${qn[t.window]}`;return Ls.has(n)?!1:(Ls.add(n),!0)}function Sx(e,t,n,r=Date.now()){if(!e||!t)return;Xl(r);let o=_D(e);for(let[s,a]of Object.entries(js))n>=qn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function ID(e,t,n=Date.now()){if(!on?.caps||!lm)return;let r=vo.get(e);if(r)for(let[o,s]of Object.entries(js)){let a=Cx(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*_x||i>=a)continue;let c=`warn:${e}:${o}:${qn[o]}`;if(!Ls.has(c)){Ls.add(c);try{let l=await lm(t);if(!l)continue;await $(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Jl(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Nx(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 Zl(e,{source:t="local",updatedAt:n=Date.now()}={}){on={caps:Nx(e),updatedAt:n,source:t};try{await cm.mkdir(Ax,{recursive:!0}),await cm.writeFile(Ex(),JSON.stringify(on,null,2))}catch{}return bi()}function bi(e=Date.now()){Xl(e);let t=new Set([...vo.keys(),...Object.keys(on?.caps||{})]),n={};for(let r of t){let o=vo.get(r);n[r]={weeklyUsd:Jl(o?.weekly||0),monthlyUsd:Jl(o?.monthly||0)}}return{caps:on?.caps||{},configured:!!on,seeded:um,updatedAt:on?.updatedAt??null,spent:n,windows:{weekly:{start:qn.weekly,resetsAt:Rx(e)},monthly:{start:qn.monthly,resetsAt:Ox(e)}},warnPct:_x}}async function Px({listTracks:e,getTrack:t}={}){lm=t||null,Xl();try{let o=JSON.parse(await cm.readFile(Ex(),"utf8"));on={caps:Nx(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{on=null}let n=Date.now();rt.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ut(o);Sx(o.provider||"unknown",s,o.ts??Date.now()),ID(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(qn.weekly,qn.monthly);for(let o of await e?.()||[]){let s;try{s=await H(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||Sx(a.provider||"unknown",Ut(a).usd,i,n)}}return um=!0,bi()}var Ax,Ex,_x,js,on,um,qn,vo,Ls,lm,_D,Jl,Ql=U(()=>{me();$r();Ax=process.env.BOSTRAT_STATE_DIR||Tx.join(process.env.HOME||ED.tmpdir(),".bostrat"),Ex=()=>Tx.join(Ax,"usage-caps.json"),_x=.8;js={weekly:{startOf:Ix,nextStart:Rx,capKey:"weeklyUsd"},monthly:{startOf:$x,nextStart:Ox,capKey:"monthlyUsd"}},on=null,um=!1,qn={weekly:0,monthly:0},vo=new Map,Ls=new Set,lm=null,_D=e=>{let t=vo.get(e);return t||(t={weekly:0,monthly:0},vo.set(e,t)),t};Jl=e=>Math.round(e*1e4)/1e4});import $D from"node:fs/promises";import RD from"node:os";import Dx from"node:path";function Wx(e){return e==="codex"?ND:e==="cursor"?Hx:Fx}function Bx(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 jx(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 PD(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 Lx(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 qx(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&&!Ux.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=Hx.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!MD.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ki(e,t,{timeoutMs:n=OD}={}){let r=Date.now()+n;for(;;){let o=await Pe(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await eu(CD)}}async function DD(e,t){return await nt(e,`/model ${t.model}`,yn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function BD(e,t){await yl(e),await nt(e,"/model",{enter:!1}),await eu(250),await We(e,["Enter"],{kind:"model-switch"});let n=await ki(e,Bx);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await We(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=PD(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await nt(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await ki(e,i=>jx(i)||!Bx(i)&&o(i)&&{done:o(i)});if(!s)throw await We(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?Ux.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 We(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await nt(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ki(e,i=>!jx(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 jD(){let e=Dx.join(process.env.CURSOR_CONFIG_DIR||Dx.join(RD.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await $D.readFile(e,"utf8"))?.model||null}catch{return null}}async function LD(e,t){await yl(e),await kl(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await ki(e,Lx),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 ki(e,Lx,{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 We(e,["Down"],{kind:"model-switch"}),await eu(250)}if(!n||!s||!o(s)){await We(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await We(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 We(e,["Enter"],{kind:"model-switch"}),await eu(800);let a=await jD();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 Kx(e,t,n){return e==="codex"?BD(t,n):e==="cursor"?LD(t,n):DD(t,n)}var eu,OD,CD,Fx,MD,ND,Ux,Hx,Gx=U(()=>{ut();ai();eu=e=>new Promise(t=>setTimeout(t,e)),OD=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,CD=300,Fx=[{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"}],MD=new Set([...Fx.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"]),ND=[{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"}],Ux=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Hx=[{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 nu(e){let t=e||{},n=FD(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function FD(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:{...Cr(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",...Cr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{change:"subtask-resumed",...Cr(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...Cr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Cr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Cr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider: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:{...Cr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Cr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function Yx(e,t){let n=nu(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;fm(()=>$(r,n))}async function Jx(e){if(!(!e?.id||Vx.has(e.id))){Vx.add(e.id);try{let t=await H(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=nu(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(e,o)}}catch{}}}function Cr(e){let{type:t,at:n,...r}=e;return r}function Xx(e,t,n){!n?.promptId||dm.get(e.id)===n.promptId||(dm.set(e.id,n.promptId),fm(()=>$(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 pm(e,t,n,r={}){dm.delete(e.id),fm(()=>$(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:gt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var gt,tu,fm,zx,Vx,dm,mm=U(()=>{me();gt=e=>e||null;tu=Promise.resolve(),fm=e=>(tu=tu.then(e).catch(()=>{}),tu),zx=()=>tu;Vx=new Set;dm=new Map});function KD(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function rv(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of KD(n).reverse()){if(!UD.test(r))continue;let o=r.endsWith("?")&&r.length<=qD&&WD.test(r);if(HD.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function sv(e){let t=String(e||""),n=[];GD.test(t)&&n.push("commit"),ov.test(t)&&n.push("rebase"),VD.test(t)&&n.push("push");let r=YD.test(t);!r&&zD.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 av(e){return ov.test(String(e||""))}function Fs(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 d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let f=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(f+=` ${d.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(f+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),f}function iv(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 cv(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<=JD&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function lv(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+XD||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function uv(e){let t=(Array.isArray(e)?e:[]).filter(r=>Zx[r]).map(r=>Zx[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 Qx,ev,tv,nv,UD,HD,WD,qD,Mr,GD,ov,VD,zD,YD,JD,XD,Zx,To=U(()=>{Qx=/(?<!\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,ev=/(?<!\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,tv=/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,nv=/(?<!\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,UD=new RegExp(`\\b(?:${Qx}|${ev}|${tv}|${nv})\\b`,"i"),HD=/\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,WD=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,qD=90;Mr=["commit","rebase","push","pr"],GD=new RegExp(`\\b(?:${Qx})\\b`,"i"),ov=new RegExp(`\\b(?:${nv})\\b`,"i"),VD=new RegExp(`\\b(?:${ev})\\b`,"i"),zD=new RegExp(`\\b(?:${tv})\\b`,"i"),YD=/\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;JD=3e4;XD=2500;Zx={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import ZD from"node:crypto";import ru from"node:fs/promises";import dv from"node:path";import QD from"node:os";async function Ue(){return Nr||(hm??=ru.readFile(gm,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Nr=e,!t&&je()){let{records:n,source:r}=await ot("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;Nr[oe(i.repo,i.number)]=i}Object.keys(Nr).length&&await ym().catch(()=>{})}}return Nr}).finally(()=>hm=null),hm)}async function ym(){let e=JSON.stringify({links:Nr},null,2),t=`${gm}.${process.pid}.tmp`;await ru.mkdir(fv,{recursive:!0}),await ru.writeFile(t,e),await ru.rename(t,gm)}async function Je(e){wm=wm.then(ym,ym),await wm.catch(()=>{});let t=Nr?.[e];t&&wt("prlinks",{id:pv(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 Ue(),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:e1.has(a),firstSeen:Date.now()},await Je(oe(e,t)),{link:i[c],created:!0})}async function Kn(e,t){if(!e||t==null)return null;let n=(await Ue())[oe(e,t)];return!n||n.announced?null:(n.announced=!0,await Je(oe(e,t)),n)}async function mv(e,t){if(!e||t==null)return!1;let n=(await Ue())[oe(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Je(oe(e,t)),!0):!1}async function st(e,t){return!e||t==null?null:(await Ue())[oe(e,t)]??null}async function Pr(e,t,n){if(!e||t==null)return null;let r=(await Ue())[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 Je(oe(e,t)),r):null}async function hv(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[oe(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Je(oe(e,t)),o):null}async function wv(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[oe(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Je(oe(e,t)),o):null}async function ou(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[oe(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Je(oe(e,t)),r)}async function su(e,t){if(!e||t==null)return null;let n=(await Ue())[oe(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Je(oe(e,t)),n)}async function au(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[oe(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Je(oe(e,t)),r)}async function iu(e,t){if(!e||t==null)return null;let n=(await Ue())[oe(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Je(oe(e,t)),n)}async function gv(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ue())[oe(e,t)];return r?(r.baseFoldAt=n,await Je(oe(e,t)),r):null}async function yv(e,t){if(!e||t==null)return null;let n=(await Ue())[oe(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Je(oe(e,t)),n)}async function bv(e,t,n){if(!e||t==null||!n)return null;let r=(await Ue())[oe(e,t)];return r?(r.lastHeadSha=n,await Je(oe(e,t)),r):null}async function kv(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Ue())[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 Je(oe(e,t))),{prev:o,link:r}}async function xi(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[oe(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Je(oe(e,t)),r):null}async function Ao(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[oe(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Je(oe(e,t)),r):null}async function Eo(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[oe(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Je(oe(e,t)),r):null}async function xv(e,t,n){if(!e||t==null)return null;let r=(await Ue())[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 Je(oe(e,t)),r}async function vv(e,t,n){if(!e||t==null)return null;let r=(await Ue())[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 Je(oe(e,t)),r}async function vi(e,t){if(!e||t==null)return null;let n=(await Ue())[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 Je(oe(e,t)),n)}async function Sv(){return Object.values(await Ue()).filter(e=>!t1.has(e.state))}async function Gn(){return Object.values(await Ue()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function Tv(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ue(),r=0;for(let[o,s]of Object.entries(n))await wt("prlinks",{id:pv(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var fv,gm,oe,pv,Nr,hm,wm,e1,t1,Vn=U(()=>{yr();fv=process.env.BOSTRAT_STATE_DIR||dv.join(process.env.HOME||QD.tmpdir(),".bostrat"),gm=dv.join(fv,"pr-links.json"),oe=(e,t)=>`${e}#${Number(t)}`,pv=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=ZD.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Nr=null,hm=null,wm=Promise.resolve();e1=new Set(["api","observed","reconciled"]);t1=new Set(["merged","closed","lost"])});import n1 from"node:fs";import bm from"node:fs/promises";import Iv from"node:path";import r1 from"node:os";function Ov(e,t=!1){if(!t&&e-Ev<o1)return;Ev=e;let n=JSON.stringify({windowStart:Us,byCaller:Object.fromEntries(zn),lastHour:Dr});_v=_v.then(()=>bm.mkdir($v,{recursive:!0})).then(()=>bm.writeFile(`${cu}.tmp`,n)).then(()=>bm.rename(`${cu}.tmp`,cu)).catch(()=>{})}function Cv(e){e-Us<Rv||(Dr={start:Us,total:lu(zn),byCaller:Object.fromEntries(zn)},Dr.total&&console.log(`[github] ${Dr.total} calls last hour`,Dr.byCaller),Us=e,zn=new Map,Ov(e,!0))}function Yn(e="other",t=Date.now()){Cv(t),zn.set(e,(zn.get(e)||0)+1),Ov(t)}function Mv(e=Date.now()){return Cv(e),{windowStart:Us,total:lu(zn),byCaller:Object.fromEntries(zn),lastHour:Dr,backoffUntil:_o>e?_o:null}}function Br(e){return s1.test(String(e?.message||e||""))}function jr(e,t=Date.now()){return _o<=t&&console.warn(`[github] rate limited \u2014 backing off ${Av/6e4} min:`,String(e?.message||e||"").slice(0,200)),_o=Math.max(_o,t+Av),_o}function uu(e=Date.now()){return Math.max(0,_o-e)}var $v,cu,Rv,Av,o1,Us,zn,Dr,_o,Ev,lu,_v,s1,Si=U(()=>{$v=process.env.BOSTRAT_STATE_DIR||Iv.join(process.env.HOME||r1.tmpdir(),".bostrat"),cu=Iv.join($v,"github-metrics.json"),Rv=60*6e4,Av=5*6e4,o1=6e4,Us=Date.now(),zn=new Map,Dr=null,_o=0,Ev=0,lu=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(n1.readFileSync(cu,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Rv?(Us=e.windowStart,zn=new Map(Object.entries(e.byCaller||{})),Dr=e.lastHour||null):lu(new Map(Object.entries(e.byCaller||{})))&&(Dr={start:e.windowStart,total:lu(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}_v=Promise.resolve();s1=/rate limit|too quickly|abuse detection|HTTP 429/i});import Nv from"node:fs/promises";import Pv from"node:path";import a1 from"node:os";function fu({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};du.push(l),du.length>c1&&du.shift();let u=km.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)),km.set(e,u),l1(l),l}async function l1(e){try{await Nv.mkdir(Dv,{recursive:!0}),await Nv.appendFile(i1,JSON.stringify(e)+`
|
|
179
|
+
`)}catch{}}function Bv(){let e={};for(let[t,n]of km)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:du.slice(-20)}}var Dv,i1,c1,du,km,pu=U(()=>{Dv=process.env.BOSTRAT_STATE_DIR||Pv.join(process.env.HOME||a1.tmpdir(),".bostrat"),i1=Pv.join(Dv,"github-latency.ndjson"),c1=100,du=[],km=new Map});import{execFile as u1}from"node:child_process";import{promisify as d1}from"node:util";async function f1(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ws("git",["-C",e,"rev-parse","HEAD"]),Ws("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 mn({cwd:e});Yn(r);try{let{stdout:a}=await Ws("gh",t,{cwd:e,...p1,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Br(a)&&jr(a),a}}async function Sm(){if(mu&&Date.now()-mu.at<6e4)return mu.data;let e;try{Yn("auth");let t=await Ws("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 Na();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return mu={at:Date.now(),data:e},e}async function m1(e){try{let{stdout:t}=await Ws("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Lv(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 xm(e){let t=(e||[]).map(Lv),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 Am(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function Uv(e,t){if(!t)return null;let n=await _e(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 Hv(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 Wv(e,t){let n=await Uv(e,t);return n===null?{...xm([]),unavailable:!0}:xm(Hv(n))}function hu(e,t=xm(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 w1(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Hs.delete(s);continue}r.add(s),Hs.has(s)||Hs.set(s,n),o.mergeabilityPending=n-Hs.get(s)<h1}for(let o of Hs.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Hs.delete(o);return t}async function qs(e,t){let n=await Jn(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",Tm]);return hu(JSON.parse(o))}catch(o){if(!Am(o))throw o;let s=await Ht(n.path,["pr","view",String(r),"--json",Fv]),a=JSON.parse(s);return hu(a,await Wv(n.path,a.headRefOid))}}async function vn(e,{caller:t="prs"}={}){try{let n=await Ht(e,["pr","list","--json",Tm,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>hu(r)).sort(jv)}catch(n){if(!Am(n))throw n;let r=await Ht(e,["pr","list","--json",Fv,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>hu(o,await Wv(e,o.headRefOid))))).sort(jv)}}function Wt(){Ti.clear()}async function b1(e,t){if(!e)return t;for(let n of t){let r=await st(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 ou(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await au(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function qv(){let e=await Sm();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await jn())await m1(a.path)&&t.push(a);let n=Date.now(),r=uu(n)>0,o=await Promise.all(t.map(async a=>{let i=Ti.get(a.path),c=i?.pending?y1:g1;if(i&&(r||n-i.at<c))return i.entry;try{let l=await _e(a.path).catch(()=>null),u=w1(a.path,await vn(a.path,{caller:"tab"}),n);if(await b1(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&&fu({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},f=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return Ti.set(a.path,{at:Date.now(),entry:d,pending:f}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Sn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Ti.keys())s.has(a)||Ti.delete(a);return{auth:e,repos:o}}async function Jn(e){let t=(await jn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Io(e){return as(e)}async function Ks(e,t,{method:n="squash"}={}){let r=await Jn(e);if(!Ai.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 mn({cwd:r.path})).GH_TOKEN){let a=await wf().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ht(r.path,s)}catch(a){throw new Error(Sn(a))}finally{Wt()}return{ok:!0,number:o}}async function Ei(e,t){let n=await Jn(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(Sn(s))}finally{Wt()}let o=await _e(n.path).catch(()=>null);return o&&await gv(o,r).catch(()=>{}),{ok:!0,number:r}}async function Em(e,t){let n=await Jn(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(Sn(o))}finally{Wt()}return{ok:!0,number:r}}function _m(e){return e.green&&!e.isDraft}function S1(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 Kv(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:_m(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Gv(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function _i(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 Vv(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(_i))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function zv(){let e={};for(let[t,n]of vm)e[t]={...n};return e}async function Yv(e,{method:t="merge",onMerged:n=null}={}){let r=await Jn(e);if(!Ai.has(t))throw new Error(`invalid merge method: ${t}`);if(vm.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await vn(r.path)).filter(_m).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 vm.set(r.path,s),A1(s,o,{listPrs:()=>vn(r.path),merge:a=>Ks(r.path,a,{method:t}),sleep:v1,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function T1(e,t){let n=Date.now()+(t.settleMaxMs??x1),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(k1)}}async function A1(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 T1(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: ${Sn(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(!_m(a)){e.skipped.push({number:a.number,title:a.title,reason:S1(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:Sn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Wt()}}async function Im(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ws("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Sn(l)}`)}await f1(e);let o=n||"";if((await mn({cwd:e})).GH_TOKEN){let l=await wf().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(Sn(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 Jv(e,t){let n=await Jn(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(Sn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Am(i))throw i;let c=await Ht(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Uv(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Sn(i));o=Hv(l).map(Lv).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=E1(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Ht(n.path,["run","view",c,"--log-failed"]).then(u=>_1(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function E1(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function _1(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
|
-
`)){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===
|
|
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
|
|
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
|
|
185
|
+
`+r.slice(-n)),r}function Sn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
186
|
+
`).find(n=>n.trim())||"gh failed"}var Ws,p1,mu,Tm,Fv,h1,Hs,jv,g1,y1,Ti,Ai,k1,x1,v1,vm,$o=U(()=>{mr();lt();Vn();Si();pu();me();Ws=d1(u1),p1={timeout:3e4,maxBuffer:8*1024*1024};mu=null;Tm="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",Fv=Tm.replace("statusCheckRollup,","");h1=9e4,Hs=new Map;jv=(e,t)=>e.number-t.number;g1=3e4,y1=1500,Ti=new Map;Ai=new Set(["squash","merge","rebase"]);k1=2500,x1=9e4,v1=e=>new Promise(t=>setTimeout(t,e));vm=new Map});import{execFile as I1}from"node:child_process";import{promisify as $1}from"node:util";async function O1(e,t){try{let{stdout:n}=await R1("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function C1(e,t,n,{rebase:r=!1,listPrs:o=vn}={}){let s=await gi(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await yi(e.dir,n).catch(()=>0),i=t?await O1(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let d=s||a>0,f=[];s&&f.push("commit"),r&&f.push("rebase"),(s||c)&&f.push("push"),d&&(u!=="open"||s||c)&&f.push("pr");let p=f.filter(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:wu.filter(h=>f.includes(h)),defaults:wu.filter(h=>p.includes(h))}}async function gu(e,{branch:t,rebase:n=!1,listPrs:r=vn}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(f=>f?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await Ns(s[0].dir).catch(()=>!1))return null;let a=[];for(let f of s)a.push(await C1(f,t,f.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(f=>f.dirty||f.aheadBase>0||f.openPr),c=i.length?i:a,l=wu.filter(f=>c.some(p=>p.available.includes(f))),u=wu.filter(f=>c.some(p=>p.defaults.includes(f))),d=c.filter(f=>f.openPr);return{available:l,defaults:u,prState:d.length?"open":c.every(f=>f.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(f=>({name:f.name,slug:f.slug,dirty:f.dirty,unpushed:f.unpushed,prState:f.prState,openPr:f.openPr}))}}var R1,wu,$m=U(()=>{zl();$o();R1=$1(I1),wu=["commit","rebase","push","pr"]});function yu(e,t,n){let{state:r,reason:o}=ip(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of Ii)a-c.at>=Xv()&&Ii.delete(i);Ii.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=Ii.get(e);return s?Date.now()-s.at>=Xv()?(Ii.delete(e),!1):s.busy:!1}async function Rm(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Pe(e,{lines:tt}).catch(()=>"");if(!yu(e,t,r))return kk(t,r)?{working:!0,prompted:!1}:n;if(xo(t,r)){let o=await Un(e).catch(()=>r),s=Or({name:e,agent:t},o);if(s)return!Gl({name:e,agent:t},s,o)||yx(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function Ro(e,t){return(await Rm(e,t)).working}var Xv,Ii,$i=U(()=>{ut();si();Xp();Qp();Xv=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Ii=new Map});import{execFile as M1}from"node:child_process";import{promisify as N1}from"node:util";async function Lr(e){if(!e)return null;let t=Zv.get(e);if(t&&Date.now()-t.at<D1)return t.branch;let n=null;try{let{stdout:r}=await P1("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return Zv.set(e,{at:Date.now(),branch:n}),n}function Qv(e){return String(e||"").match(B1)?.[1]??null}function j1(e,t){let n=Qv(e);return!!n&&!!t&&n===t}function eS(e,t){let n=Qv(e);return!!n&&n!==t}function L1(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 Gs(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of L1(e))n(await Lr(r));return t}async function bu(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=>j1(t,a.id));if(s)return s;for(let a of r)if(!Ge(a)&&(await Gs(a)).includes(t))return a;return null}var P1,D1,Zv,B1,Oo=U(()=>{xn();P1=N1(M1),D1=5e3,Zv=new Map;B1=/^track\/.+-([0-9a-f]{8})$/});import{execFile as F1}from"node:child_process";import{promisify as U1}from"node:util";function Vs({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function xu(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function Su({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?V1[r]:null;return a&&s.push(a),s.push(`${sS}: ${Vs({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${aS}: ${String(o).trim()}`),s}function tS(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===sS.toLowerCase()&&!t.epoch)t.epoch=xu(a);else if(s===aS.toLowerCase()&&!t.model)t.model=a;else if(s===iS.toLowerCase()&&!t.cause){let i=a.toLowerCase();Ri.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 z1(e,t=Fr){return e<=t?"rework":"drift"}function Y1(e,{window:t=Fr,end:n=Pm}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function Tu({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return J1.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${iS} trailer`}:Z1.test(s)&&!nS.test(s)?{cause:"style",level:"C",basis:"subject"}:nS.test(s)?{cause:"defect",level:"C",basis:"subject"}:X1.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 Bm({heuristic:e=null,subject:t="",ref:n=null,promptKind:r=null,fixerCategory:o=null,ticketKind:s=null}={}){let a=String(t||""),i=Q1.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&&eB.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&tB.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"}:rS.test(String(n||""))||rS.test(a)?{intent:"planned",basis:"blueprint item"}:o&&nB.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function aB(e){let t=String(e||"");return rB.some(n=>n.test(t))?"excluded":oB.some(n=>n.test(t))?"docs":sB.some(n=>n.test(t))?"config":"code"}function iB(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 cB(e,t){let n=Nm(e),r=Nm(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>ku&&(n=n.slice(0,ku)),r.length>ku&&(r=r.slice(0,ku));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 lB(e,t=cS){let n=new Set,r=e?.del||[],o=e?.add||[];for(let s=0;s<r.length;s++){let a=e.oldStart+s;if(!Nm(r[s]).length){n.add(a);continue}s<o.length&&cB(r[s],o[s])>=t&&n.add(a)}return n}function uB(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 dB(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 oS(e,t){let{stdout:n}=await Mm(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
190
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)}
|
|
191
|
+
`)}}async function lS(e,t,{fixKey:n=null,window:r=Fr,resolveOrigin:o=null,context:s={}}={}){let a=await oS(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=tS(a.message),l=n?xu(n):c.epoch,u=l?Vs(l):null,{stdout:d}=await Mm(e,["diff","-U0","--no-color","-M","-C",i,t]),f=iB(d),p=0,h=0,w=0,g=0,k=0,x=0,y=0,_=new Map;for(let I of f){if(I.binary)continue;let C=I.oldPath||I.newPath||"",J=I.deleted.reduce((G,[,Te])=>G+Te,0),q=aB(C);if(q==="excluded"){w+=J;continue}if(q==="docs"){g+=J,k+=I.added;continue}if(q==="config"){x+=J;continue}p+=I.added;let R=new Set;for(let G of I.hunks||[])for(let Te of lB(G))R.add(Te);y+=R.size;let W=[];for(let[G,Te]of I.deleted)for(let le=G;le<G+Te;le++)R.has(le)||W.push(le);if(h+=W.length,!I.oldPath||!W.length)continue;let X;try{let G=["blame","-w","-M","-C","--line-porcelain"];for(let[Te,le]of uB(W))G.push("-L",`${Te},${Te+le-1}`);G.push(i,"--",I.oldPath),X=dB((await Mm(e,G)).stdout)}catch{continue}for(let{sha:G,time:Te}of X.values()){let le=_.get(G)??{lines:0,time:Te};le.lines+=1,_.set(G,le)}}let M=[],j=0,L=new Map;for(let[I,{lines:C,time:J}]of _){let q=L.get(I);if(!q){try{q=await oS(e,I)}catch{q=null}L.set(I,q)}let R=q?tS(q.message):{epoch:null},W=R.epoch,X=R.provider,G=R.model;if(!W&&o){let K=await o(I,q);K?.trackId&&K?.boxId!=null&&(W={trackId:K.trackId,boxId:K.boxId,epoch:K.epoch??0},X=X??K.provider??null,G=G??K.model??null)}if(!W){j+=C;continue}let Te=Number.isFinite(J)?J:q?.ts??a.ts,le=Math.max(0,(a.ts-Te)/q1),be=Vs(W),ae=be===u?"self":z1(le,r);M.push({sha:I,key:be,trackId:W.trackId,boxId:W.boxId,epoch:W.epoch,provider:X??null,model:G??null,lines:C,ageDays:Math.round(le*100)/100,class:ae,decay:ae==="rework"?1:ae==="drift"?Math.round(Y1(le,{window:r})*1e3)/1e3:0})}M.sort((I,C)=>C.lines-I.lines);let F=Tu({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=Bm({heuristic:F,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:y,docsDeleted:g,docsAdded:k,configDeleted:x,codeDelta:p+h+y>0,origins:M,unattributedLines:j,ghost:h===0&&p>0,heuristic:F,intent:A}}function uS(e,{minLines:t=Dm}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return zs.has(n)?Oi(e)>=t:!1}function Oi(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Cm(e){let t=e.genUsd;return{...e,genUsd:sn(t),reworkChargedUsd:sn(e.reworkChargedUsd),reworkPendingUsd:sn(e.reworkPendingUsd),trueUsd:sn(t+e.reworkChargedUsd),multiplier:t>0?sn((t+e.reworkChargedUsd)/t):null}}function dS(e,{window:t=Fr,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 I of i(A.payload))l.add(I);else if(A.type==="rework.cause.answered"&&A.payload?.cause)for(let I of i(A.payload))c.set(I,A.payload.cause);else if(A.type==="rework.cause.retracted")for(let I of i(A.payload))c.delete(I);let u={},d=(A,I,C)=>Vs({trackId:A,boxId:I,epoch:C??0}),f=(A,I={})=>{if(!u[A]){let J=xu(A)||{};u[A]={key:A,...J,provider:null,model:null,...Om()}}let C=u[A];return I.provider&&!C.provider&&(C.provider=I.provider),I.model&&!C.model&&(C.model=I.model),C};for(let A of o){if(!A.boxId)continue;let I=d(A.trackId,A.boxId,A.epoch),{usd:C,model:J}=Ut(A),q=f(I,{provider:A.provider,model:J!=="unknown"?J:null});q.genUsd+=C}let p=new Map;for(let A of s){let I=`${A.trackId}/${A.boxId}`;(p.get(I)??p.set(I,[]).get(I)).push({...A,ts:A.payload?.ts??A.ts})}for(let A of p.values())A.sort((I,C)=>I.ts-C.ts);let h=(A,I,C,J)=>{let q=p.get(`${A}/${I}`)||[],R=-1/0;for(let G of q)G.ts<J&&(R=G.ts);let W=0,X=0;for(let G of o)G.trackId!==A||G.boxId!==I||(G.epoch??0)===(C??0)&&(G.ts<=R||G.ts>J||(W+=Ut(G).usd,X++));return{usd:W,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},k={fix:0,"likely-fix":0,planned:0,unknown:0},x=0,y=[],_=[],M={},j={},L=new Set;for(let A of a){let I=A.payload||{};if(!I.sha||L.has(I.sha))continue;if(L.add(I.sha),I.codeDelta===!1){x++;continue}let C=xu(I.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:J,turns:q}=h(C.trackId,C.boxId,C.epoch,I.ts??A.ts),R=G1.includes(I.intent?.intent)?I.intent.intent:"unknown";k[R]+=1;let W=null;c.has(I.sha)?W={cause:c.get(I.sha),level:"B",basis:"operator"}:(I.heuristic?.level&&I.heuristic.level!=="C"||I.heuristic)&&(W=I.heuristic);let X=W?.level??"none";g[X]=(g[X]||0)+1;let G=K1.has(X),Te=Number(I.deleted)||0,le=Array.isArray(I.origins)?I.origins:[],be=le.filter(K=>K.class==="rework").reduce((K,ue)=>K+ue.lines,0),ae={sha:I.sha,subject:I.subject??"",ts:I.ts??A.ts,trackId:A.trackId,fixKey:I.fixKey??null,fixProvider:I.fixProvider??A.provider??null,fixUsd:sn(J),fixTurns:q,linesReworked:be,unattributedLines:Number(I.unattributedLines)||0,ghost:I.ghost===!0,intent:{intent:R,basis:I.intent?.basis??null},verdict:W?{cause:W.cause,level:X,basis:W.basis??null}:null,charged:sn(0),origins:[]};if(R==="planned"||W?.cause===vu){w.planned+=J;for(let K of le)K.class==="rework"&&ae.origins.push({key:K.key,provider:K.provider??null,model:K.model??null,lines:K.lines,ageDays:K.ageDays,class:K.class,usd:0})}else if(I.ghost||Te===0)w.ghost+=J;else{let K=0;for(let re of le){let V=re.lines/Te,$e=J*V;if(K+=$e,re.class==="self"){w.self+=$e;continue}let Me=re.class==="rework"?1:Number(re.decay)||0,fe=$e*Me;if(w.decayed+=$e-fe,fe<=0)continue;let E=f(re.key,{provider:re.provider,model:re.model});E.fixes+=1,E.linesReworked+=re.class==="rework"?re.lines:0;let P=W?.cause&&Ri.includes(W.cause)?W.cause:"unclassified";E.causes[P]+=1,G?(E.reworkChargedUsd+=fe,w.charged+=fe,ae.charged=sn(ae.charged+fe)):(E.reworkPendingUsd+=fe,w.pending+=fe),ae.origins.push({key:re.key,provider:E.provider,model:E.model,lines:re.lines,ageDays:re.ageDays,class:re.class,usd:sn(fe)})}let ue=Math.max(0,J-K);w.unattributed+=ue}y.push(ae),!c.has(I.sha)&&l.has(I.sha)&&_.push(I.sha)}for(let A of Object.values(u)){let I=A.provider||"unknown",C=A.model||"unknown",J=q=>{q.genUsd+=A.genUsd,q.reworkChargedUsd+=A.reworkChargedUsd,q.reworkPendingUsd+=A.reworkPendingUsd,q.linesReworked+=A.linesReworked,q.fixes+=A.fixes;for(let R of Object.keys(q.causes))q.causes[R]+=A.causes[R]};J(M[I]||=Om()),J(j[C]||=Om())}y.sort((A,I)=>I.ts-A.ts);let F=Object.values(u).map(A=>{let I=n[`${A.trackId}/${A.boxId}`]||{};return Cm({...A,trackName:I.trackName??null,label:I.label??null})}).sort((A,I)=>I.reworkChargedUsd+I.reworkPendingUsd-(A.reworkChargedUsd+A.reworkPendingUsd)||I.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${cS} 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:k,noCodeDelta:x,byEpoch:Object.fromEntries(F.map(A=>[A.key,A])),epochs:F,byProvider:Object.fromEntries(Object.entries(M).map(([A,I])=>[A,Cm(I)])),byModel:Object.fromEntries(Object.entries(j).map(([A,I])=>[A,Cm(I)])),buckets:Object.fromEntries(Object.entries(w).map(([A,I])=>[A,sn(I)])),fixUsdTotal:sn(Object.values(w).reduce((A,I)=>A+I,0)),fixes:y,verdicts:g,pendingTaps:_,commits:s.length}}var H1,W1,Mm,sS,aS,iS,q1,Fr,Pm,Dm,Ri,vu,K1,G1,zs,cS,V1,J1,nS,X1,Z1,Q1,eB,tB,rS,nB,rB,oB,sB,Nm,ku,sn,Om,Au=U(()=>{$r();H1=U1(F1),W1=64*1024*1024,Mm=(e,t)=>H1("git",["-C",e,...t],{maxBuffer:W1,timeout:6e4}),sS="Bostrat-Epoch",aS="Bostrat-Model",iS="Bostrat-Fix-Cause",q1=24*36e5,Fr=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Pm=90,Dm=3,Ri=Object.freeze(["defect","design","requirements","style"]),vu="not-a-fix",K1=new Set(["A","B","E"]),G1=Object.freeze(["fix","likely-fix","planned","unknown"]),zs=new Set(["fix","likely-fix"]),cS=.8;V1={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};J1=/^revert\b|^revert "/i,nS=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,X1=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,Z1=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;Q1=/^([a-z]+)(\([^)]*\))?!?:\s/i,eB=new Set(["fix","hotfix","bugfix","revert"]),tB=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),rS=/\bBP-[A-Z]*\d+\b/i,nB=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);rB=[/(^|\/)\.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)\//],oB=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],sB=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Nm=e=>String(e??"").replace(/\s+/g," ").trim(),ku=400;sn=e=>Math.round(e*1e4)/1e4,Om=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as fB}from"node:child_process";import{promisify as pB}from"node:util";function fS(e,t=Date.now()){return xf(e,t)??0}function hB(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 pS(e,t,{now:n=Date.now()}={}){let r=fS(t,n),o=await H(e);return Su({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:hB(o,t.name,r)})}function wB(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 gB(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=wB(n,t.name),c=i?Hn(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function yB(e,t){try{let{stdout:n}=await jm(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 bB(e){try{let{stdout:t}=await jm(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function mS(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 H(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 jm(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=fS(t,i),l=await yB(r,n),u=await bB(r),d=await $(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:u,...l,ts:i}}),f=null,p=!1;try{let h=await lS(r,n,{fixKey:Vs({trackId:e.id,boxId:t.name,epoch:c}),window:Fr,context:{...gB(e,t,a,c),ref:u,provider:t.agent??null}});if(h&&(f=await $(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:h}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&uS(h))){let g=Oi(h),k=h.origins.filter(x=>x.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:h.subject,linesReworked:g,origins:k.map(x=>({key:x.key,provider:x.provider,model:x.model,lines:x.lines,ageDays:x.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:d,rework:f,offered:p}}function hS(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 wS(e,t,n){if(!Ri.includes(n)&&n!==vu)throw new Error(`bad cause "${n}" (${Ri.join("|")}|${vu})`);let r=await H(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(hS(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 gS(e,t){let n=await H(e);if(!hS(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 Eu(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 H(e);if(c.some(x=>x.type==="rework.cause.offered"&&x.payload?.pr?.number===n&&(t==null||x.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let x of c)if(x.type==="rework.cause.offered")for(let y of Array.isArray(x.payload?.shas)?x.payload.shas:[x.payload?.sha])y&&l.add(y);let u=new Map;for(let x of c)x.type==="code.committed"&&x.payload?.sha&&u.set(x.payload.sha,x.payload.ref??null);let d=Bm({heuristic:Tu({subject:r}),subject:r,ref:o}),f=[];for(let x of c){if(x.type!=="code.rework")continue;let y=x.payload||{};if(!y.sha||l.has(y.sha)||y.codeDelta===!1||o&&u.has(y.sha)&&u.get(y.sha)!==o)continue;let _=y.intent?.intent??"unknown";(zs.has(_)||_==="unknown"&&zs.has(d.intent))&&Oi(y)!==0&&f.push(y)}let p=f.reduce((x,y)=>x+Oi(y),0);if(p<Dm)return{offered:!1,reason:"below-threshold"};let h=new Map;for(let x of f)for(let y of x.origins??[]){if(y.class!=="rework")continue;let _=h.get(y.key)??{key:y.key,provider:y.provider??null,model:y.model??null,lines:0,ageDays:y.ageDays};_.lines+=y.lines,_.ageDays=Math.min(_.ageDays,y.ageDays),h.set(y.key,_)}let w=f.map(x=>x.sha),g=f.map(x=>x.heuristic).find(x=>x&&x.level!=="C")??Tu({subject:r})??f.map(x=>x.heuristic).find(Boolean)??null,k=zs.has(d.intent)?d:f.find(x=>zs.has(x.intent?.intent))?.intent??d;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)||f[f.length-1].subject,linesReworked:p,origins:[...h.values()].sort((x,y)=>y.lines-x.lines).slice(0,3),heuristic:g,intent:k,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 mB,jm,_u=U(()=>{me();Ol();Au();mB=pB(fB),jm=(e,t)=>mB("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as kB}from"node:child_process";import{promisify as xB}from"node:util";async function Lm(e,t,{midTurn:n=Ro,pollMs:r=vB,capMs:o=SB}={}){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 SS(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){yS.lastIndex=n;let o=yS.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*":"")+RB(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 Fm(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function AS(e){let t=SS(e),n=t.match(_B);if(!n)return null;let r=Fm(t,n.index);return $B.test(r)?null:{repo:r.match(TS)?.[1]??null,head:r.match(OB)?.[1]??null}}function ES(e){let t=SS(e),n=t.match(IB);if(!n)return null;let r=Fm(t,n.index),o=Fm(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("-")){MB.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(TS)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function PB(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Yn("observer");let{stdout:c}=await bS("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",NB],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Br(l)&&jr(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-TB&&!n(c)),s=await mn({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 DB(e,t){if(t)return[t];let n=[];for(let r of _S(e)){let o=await _e(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await jn().catch(()=>[])){let o=await _e(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function _S(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 BB(e,t,n){let r=await Gs(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)||CB(c.headRefName,e.id),i=[];for(let c of await DB(e,t?.repo??null)){let l;try{l=await PB(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await st(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function IS(){for(let e of[...Um])e()}function $S(e,t,{delays:n=kS,turnEnd:r}={}){let o=AS(t.command);if(!o)return null;let s=!!ES(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Ur.has(a))return null;Ur.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>{let g=()=>{clearTimeout(k),Um.delete(g),w()},k=setTimeout(g,l);Um.add(g)});let u=await BB(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:h}=await Rt({repo:d,number:f.number,trackId:e.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!h&&!(p?.trackId===e.id&&await Kn(d,f.number))?null:(await Lm(t.box,t.agent,r),await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{Ur.delete(a)}})();return c.catch(()=>Ur.delete(a)),c}function RS(e,t,{delays:n=kS,turnEnd:r,openedWait:o}={}){let s=ES(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Ur.has(a))return null;Ur.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let d=[];if(c)d=[c];else{let h=_S(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let g=await _e(w).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await Gs(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(h,w)=>{let g=await st(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 xi(c,h.number),w&&((await H(e).catch(()=>[])).some(y=>y.type==="pr.state.changed"&&(y.payload?.change==="auto-armed"||y.payload?.change==="merged")&&y.payload?.number===h.number)||(await Lm(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(j=>setTimeout(j,h));let w=[];for(let j of d)try{Yn("observer");let{stdout:L}=await bS("gh",["pr","view",String(l),"--repo",j,"--json",jB],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await mn({slug:j})}}),F=JSON.parse(L||"null");F?.number&&w.push({slug:j,pr:F})}catch(L){Br(L)&&jr(L)}let g=w.find(j=>j.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 f(k,!0);continue}let x=await st(c,k.number);if(x&&x.trackId!==e.id)return null;x||await Rt({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Lm(t.box,t.agent,r);let y=await H(e).catch(()=>[]);if(y.some(j=>j.type==="pr.state.changed"&&j.payload?.change==="merged"&&j.payload?.number===k.number))return null;let _=j=>j.some(L=>L.type==="pr.state.changed"&&L.payload?.change==="opened"&&L.payload?.number===k.number),M=0;if(!_(y)){if(AS(t.command)&&await Kn(c,k.number)){let j=Date.now();M=j+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:j,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await st(c,k.number))?.announced){let j=o?.capMs??AB,L=o?.pollMs??EB,F=Date.now()+j;for(;Date.now()<F&&(await new Promise(A=>setTimeout(A,L)),!_(await H(e).catch(()=>[]))););}}return await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),M),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Eu(e,{repo:c,number:k.number,title:k.title,branch:k.headRefName,url:k.url}).catch(()=>{}),await Zn(c,k.number).catch(()=>{}),await Xn(c,k.number).catch(()=>{}),await Pr(c,k.number,"merged"),k}return p?f(p,!1):null}finally{Ur.delete(a)}})();return i.catch(()=>Ur.delete(a)),i}var bS,kS,vB,SB,TB,AB,EB,xS,vS,_B,IB,$B,yS,RB,TS,OB,CB,MB,NB,Ur,Um,jB,Hm=U(()=>{me();mr();lt();Si();Vn();Qn();$i();Oo();_u();bS=xB(kB),kS=[4e3,15e3,45e3],vB=2500,SB=5*6e4;TB=15e3,AB=9e4,EB=2e3,xS=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,vS=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,_B=new RegExp(`${xS}${vS}create\\b`),IB=new RegExp(`${xS}${vS}merge\\b`),$B=/\s--web\b/,yS=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,RB=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");TS=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,OB=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,CB=eS;MB=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);NB="number,url,title,headRefName,createdAt";Ur=new Set,Um=new Set;jB="number,url,title,headRefName,state"});import LB from"node:fs/promises";import qm from"node:path";import FB from"node:os";function Wm(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Iu(e){let t=UB.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Wm(n,"from"),s=o?o.replace(/^uds:/,""):null,a=OS(s);return{socket:s,pid:a,name:Wm(n,"from-name"),mode:Wm(n,"from-mode"),text:r.trim()}}function OS(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function HB(){return process.env.CLAUDE_CONFIG_DIR?qm.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):qm.join(FB.homedir(),".claude","sessions")}async function WB(e){let t=OS(e);if(!t)return null;try{let n=await LB.readFile(qm.join(HB(),`${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 CS(e,t){let n=await WB(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 UB,Km=U(()=>{UB=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function Gm(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Vm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Gm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),MS);try{let a=await NS(`${Re(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 PS(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(!Gm(s))return{ok:!1,source:"unconfigured"};let i=$u.get(e.id)||0;if(a()-i<qB)return{ok:!1,source:"debounced"};$u.set(e.id,a());let c=await Ci({env:s}),l=c.loops.find(f=>f.trackId===e.id);if(!l)return $u.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),d=await Vm({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||$u.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function Ci({env:e=process.env}={}){if(!Gm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),MS);try{let r=await NS(`${Re(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 MS,NS,qB,$u,zm=U(()=>{lt();MS=1e4,NS=(...e)=>fetch(...e);qB=10*6e4,$u=new Map});function DS(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function BS(e,t,n=Ym){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await GB(`${Re(t)}${e}`,{headers:VB(t),signal:r.signal})}finally{clearTimeout(o)}}async function Mi({env:e=process.env,force:t=!1,timeoutMs:n=Ym}={}){if(!DS(e))return{followups:[],source:"unconfigured"};if(!t&&Co&&Date.now()-Co.at<KB)return{followups:Co.followups,source:"cache"};try{let r=await BS("/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 Co={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Co?{followups:Co.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function jS(e,{env:t=process.env,timeoutMs:n=Ym}={}){if(!e)return{followup:null,source:"unconfigured"};if(!DS(t))return{followup:null,source:"unconfigured"};try{let r=await BS(`/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=Co?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var KB,Ym,Co,GB,VB,LS=U(()=>{lt();KB=6e4,Ym=5e3,Co=null,GB=(...e)=>fetch(...e);VB=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import{randomUUID as Js,createHash as zB}from"node:crypto";function WS(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function QB(e){for(let[t,n]of ZB)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 qS(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Ys(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>FS)throw new Error(`label must be \u2264${FS} characters`);let o=Ys(n.trigger)||(n.ship?"now":""),s=t?JB:HS.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"?tj(n.ship):null,i=Ys(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>US)throw new Error(`prompt must be \u2264${US} bytes`);if(t){let u=QB(i);if(u)throw new Error(u)}let c=Ys(n.why).slice(0,YB)||null,l=t?ej(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function ej(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 tj(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):Jm.slice();for(let i of n)if(!Jm.includes(i))throw new Error(`ship.actions may only contain ${Jm.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=Ys(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=>Ys(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Ru(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 KS(e,t,{pull:n=Mi,ship:r={}}={}){if(Qm(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=qS(t);if(o.ship)return{kind:"ship",...await oj(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=Js(),k=Js();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:Xm(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=WS(o.label),d=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(d)return{kind:"covered",covered:{id:d.id,label:d.label},...o};let f=await H(s),p=Ru(f,{trigger:o.trigger}),h=p.length?p[p.length-1].armId:null,w=Js();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 GS(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await H(n);if(!Ru(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 VS(e,t,{pull:n=Mi}={}){if(Qm(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=qS(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await H(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=WS(r.label),d=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),f=d.find(g=>g.status==="deleted"),p=d.find(g=>g.status!=="deleted");if(p)throw new Error(`"${p.label}" (${Zm[p.trigger]||p.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(f){let g=f.deletedAt?new Date(f.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${f.label}" on ${g} \u2014 do not re-propose it`)}let h=Js(),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 zS(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await H(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 Ni(e,t,n={},{pull:r=Mi}={}){if(!e||Qm(e))return null;let o={id:e.id,cwd:e.cwd},s=await H(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null;if(t==="pr.merged"){let f=w=>{let g=(w?.prs||[]).filter(k=>Number.isFinite(Number(k?.number))).map(k=>`${k?.repo??w?.repo??""}#${Number(k.number)}`);return Number.isFinite(Number(w?.prNumber))&&g.push(`${w?.repo??""}#${Number(w.prNumber)}`),g},p=new Set(f(n));if(s.some(w=>w.type==="followup.offered"&&w.payload?.trigger==="pr.merged"&&f(w.payload?.context).some(g=>p.has(g))))return null}let i=Ru(s,{trigger:t}).map(f=>({armId:f.armId,label:f.label,prompt:f.prompt,promptHash:Xm(f.prompt),origin:"agent",why:f.why??null})),c=[];if(t!=="now"){let{followups:f}=await r().catch(()=>({followups:[]}));c=f.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:nj(p.prompt),promptHash:Xm(p.prompt),origin:p.origin,repo:p.repo??null}))}let l=[...i,...c].slice(0,XB);if(!l.length)return null;let u=Js(),d={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:d}),d}function rj(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 YS(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=jS}=s;if(!t)throw new Error("offerId required");let l={id:e.id,cwd:e.cwd},u=await H(l),d=u.find(y=>y.type==="followup.offered"&&y.payload?.offerId===t);if(!d)throw new Error("unknown offer");if(u.some(y=>y.type==="followup.answered"&&y.payload?.offerId===t))throw new Error("offer already answered");let f=d.payload.rows||[],p=y=>$(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...y}});if(!n&&!r)return await p({action:"dismiss",followupIds:f.map(y=>y.followupId).filter(Boolean),armIds:f.map(y=>y.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let h=null,w=null;if(n){if(!f.some(M=>M.followupId===n))throw new Error("that follow-up is not on this card");let{followup:y,source:_}=await c(n);if(_==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!y||y.status!=="active"){await p({action:"removed",followupId:n});let M=new Error("removed in Settings since it was offered");throw M.code="removed",M}h=y.prompt,w=y.label}else{let y=f.find(M=>M.armId===r);if(!y)throw new Error("that arm is not on this card");if(h=u.find(M=>M.type==="followup.armed"&&M.payload?.armId===r)?.payload?.prompt??y.prompt,w=y.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=`${rj(d.payload)}
|
|
193
193
|
|
|
194
|
-
${h}`,
|
|
194
|
+
${h}`,x=await a(e.id,k,{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:!!x?.queued}}async function oj(e,t,{changedRepos:n=null,probe:r=gu,branchOf:o=Lr}={}){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(()=>[]),k=s.repos?g.filter(x=>s.repos.includes(x.name)):g;l=k.length?k.map(x=>({name:x.name,slug:x.repo??null})):void 0}let u=(c?null:await o(e.cwd).catch(()=>null))||e.workspace.branch,d=s.rebase==="offered",f=await r(e,{branch:u,rebase:d}).catch(()=>null),p=[...d?["rebase"]:[],...s.actions].filter(g=>Mr.includes(g));if(f&&f.available.length===0)return{skipped:!0,reason:"nothing to ship \u2014 the tree is clean, pushed, and its PR is open",shipState:f};let h=Js(),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:f?.available,defaults:f?f.defaults.filter(g=>p.includes(g)||g==="rebase"):void 0,prState:f?.prState,openPr:f?.openPr||void 0,repos:f?.repos||l,offered:f?p.filter(g=>f.available.includes(g)):p,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:h,branch:u,offered:p,available:f?.available??null,prState:f?.prState??null}}function JS(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 XS(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 ZS(e,{pull:t=Mi}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await H({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?Ru(s):[]}}var FS,US,YB,HS,JB,Zm,XB,Jm,Xm,Ys,ZB,Qm,nj,Ou=U(()=>{me();To();$m();Oo();LS();FS=60,US=8*1024,YB=200,HS=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],JB=HS.filter(e=>e!=="now"&&e!=="track.opened"),Zm={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"},XB=3,Jm=["commit","push","pr"];Xm=e=>zB("sha256").update(String(e)).digest("hex").slice(0,16),Ys=e=>typeof e=="string"?e.trim():"",ZB=[[/(^|[^\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"]];Qm=e=>String(e?.name||"").startsWith("\u21BB");nj=e=>String(e||"").split(`
|
|
195
195
|
`).slice(0,3).join(`
|
|
196
|
-
`).slice(0,400)});import{randomUUID as
|
|
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,a.ts),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,l.ts??void 0)),!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()===
|
|
196
|
+
`).slice(0,400)});import{randomUUID as sj}from"node:crypto";function Pi(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 eT(e){return`job|${e.box}|id:${e.id}`}async function th(e){return eh.has(e.id)?eh.get(e.id):(Cu.has(e.id)||Cu.set(e.id,H(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(Pi({role:o,sourceId:r.payload.sourceId})),n.keys.add(Pi({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(Pi({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Pi({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(eT({id:r.payload.id,box:r.boxId}));return eh.set(e.id,n),n}).finally(()=>Cu.delete(e.id))),Cu.get(e.id))}async function tT(e,t){let n=new Set;try{let r=(await th(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=Pi(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?Iu(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 CS(a,Di),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=tk(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}}),PS(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=rv(o.text);if(c&&JS(await H(e).catch(()=>[])))continue;if(c){let l=av(c),u;if(e.workspace?.kind==="multi"){let h=await Mo(e).catch(()=>[]);u=h.length?h.map(w=>({name:w.name,slug:w.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await Lr(e.cwd).catch(()=>null))||e.workspace.branch,f=await gu(e,{branch:d,rebase:l}).catch(()=>null),p=f?null:sv(c);if(f&&f.available.length===0)continue;await $(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:sj(),ts:o.ts,excerpt:c,branch:d,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:f?.available,defaults:f?.defaults,prState:f?.prState,openPr:f?.openPr||void 0,repos:f?.repos||u,offered:f?.defaults||p?.offered||void 0,toPr:p?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function nT(e,t){try{let n=await th(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}}),$S(e,o),RS(e,o))}}catch{}}async function rT(e,t){try{let n=await th(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=eT(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 eh,Cu,oT=U(()=>{me();To();$m();Hm();Oo();Km();Qn();vl();zm();Ou();eh=new Map,Cu=new Map});function aj(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 ij(e,t){let n=`${e.id}:${t.name}`;if(!nh.has(n)){let r=await H(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);nh.set(n,o)}return nh.get(n)}async function sT(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await uo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await ij(e,t),o=aj(await Tt(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:kn(c,i,a.ts),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 nh,aT=U(()=>{me();po();nn();Wn();nh=new Map});import Mu from"node:fs";import oh from"node:path";import cj from"node:os";async function dj({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(uj,{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:iT})});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<iT)break}return r}function fj(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 lT(){try{return JSON.parse(Mu.readFileSync(Nu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function mj(e){try{Mu.mkdirSync(oh.dirname(Nu()),{recursive:!0});let t=Nu()+".tmp";Mu.writeFileSync(t,JSON.stringify(e)),Mu.renameSync(t,Nu())}catch{}}async function wj({fetchImpl:e,now:t=Date.now()}={}){if(Hr.rows&&t-Hr.at<pj)return Hr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=lT(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await dj({sinceMs:o,cookie:n,fetchImpl:e});Hr={at:t,rows:s.map(fj).filter(Boolean),error:null}}catch(s){Hr={at:t,rows:null,error:String(s?.message||s)}}return Hr.rows}async function gj(e,t){let n=`${e.id}:${t.name}`;if(!rh.has(n)){let r=await H(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);rh.set(n,o)}return rh.get(n)}async function uT(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!cT())return;let r=await wj({fetchImpl:n});if(!r||!r.length)return;let o=await J0(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await gj(e,t),a=lT(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=kn(u,d,l.ts??void 0)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await $(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}mj({sinceMs:c,seen:[...i].slice(-hj)})}catch{}}var lj,cT,uj,iT,pj,Hr,Nu,hj,sh,rh,ah=U(()=>{me();Bp();Wn();lj=process.env.BOSTRAT_STATE_DIR||oh.join(process.env.HOME||cj.tmpdir(),".bostrat"),cT=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,uj="https://cursor.com/api/dashboard/get-filtered-usage-events",iT=100;pj=5*6e4,Hr={at:0,rows:null,error:null},Nu=()=>oh.join(lj,"cursor-usage.json");hj=5e3;sh=()=>({enabled:cT(),lastPollAt:Hr.at||null,error:Hr.error}),rh=new Map});import Li from"node:fs/promises";import At from"node:path";import yj from"node:os";import bj from"node:net";import{randomUUID as kj}from"node:crypto";import{execFile as xj}from"node:child_process";import{promisify as vj}from"node:util";function $j(e){for(let t of ph(e))if(!wT.test(t))return t;return null}function gT(e){let t=ph(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function Rj(e,t){let n=gT(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 Cj(e){let t=!1,n=!1;for(let r of ph(e)){if(!t){if(wT.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&ih(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,ih(r))return`serves the filesystem root ("${r}")`;continue}if(ih(r))return`serves the filesystem root ("${r}")`;if(Oj(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function Nj(e,t){if(At.resolve(e)===At.resolve(t))return!0;let[n,r]=await Promise.all([Pu(e),Pu(t)]);return n===r}async function Pj(e){let n=(await Li.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 yT(At.join(e,o.name,uh))?`./${o.name}`:null))).filter(Boolean)}async function Dj(e,t){let[n,r]=await Promise.all([Pu(e),Pu(t)]),o=At.relative(n,r);if(o&&(o.startsWith("..")||At.isAbsolute(o)))throw Bi(`serve directory escapes the Track workspace: ${t}`)}async function Bj(e,t){let n=Cj(t);if(n)throw Bi(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await Mj(e))throw Bi(`serve directory does not exist: ${e}`);let r=$j(t);if(!r)return;if(!r.includes("/")&&!await $b(r,{loginShell:!0,failOpen:!0}))throw Bi(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(Ej.test(t)||_j.has(r)||!Aj.has(r)||!gT(t).includes("run")||Ij.test(t))return;let o=Rj(e,t);if(await yT(At.join(o,uh)))return;let s=await Pj(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Bi(`no ${uh} in ${o}${a}`)}async function nr(){return Tn||(ch||=(async()=>{try{Tn=JSON.parse(await Li.readFile(hT,"utf8"))}catch{Tn={views:[],adoptWatermarks:{}}}finally{ch=null}return Tn.views||=[],Tn.adoptWatermarks||={},Tn})(),ch)}async function En(){await St(hT,Tn)}function tr(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 hh(e){return ji.get(e)||null}function kT(e,{name:t,box:n}={}){let r=ji.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 ji.set(e,o),o}function dh(e,t){let n=ji.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Du(e,{error:t=null}={}){let n=ji.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function xT(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 fh(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function jj(e,t){let n=fh(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!pT.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!pT.has(s))return s}}function Fi(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=bj.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 wh(e){if(process.platform!=="darwin"){await An("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await An("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 vT(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function Lj(e,t){let n=new Set(e.views.map(o=>o.port)),r=vT();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=dT;o<=fT;o++)if(!(n.has(o)||r.has(o))&&!await Fi(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${dT}-${fT}`)}async function Fj(e){try{let{stdout:t}=await An(Y,Z("list-sessions","-F","#{session_name}"));return t.split(`
|
|
198
|
+
`).some(n=>n.trim()===Xs(e))}catch{return!1}}async function Ui(e){try{await An(Y,Z("kill-session","-t",Xs(e)))}catch{}}async function gh(e){if(!await Ya())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Xs(e.slug),n=e.serveCwd??e.cwd,r=At.dirname(process.execPath);await An(Y,Z("new-session","-d","-s",t,"-c",n)),await An(Y,Z("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await An(Y,Z("send-keys","-t",t,"Enter")),await An(Y,Z("send-keys","-t",t,"-l",e.cmd)),await An(Y,Z("send-keys","-t",t,"Enter"))}async function mT(e,t=12){try{let{stdout:n}=await An(Y,Z("capture-pane","-t",Xs(e),"-p"));return String(n||"").trimEnd().split(`
|
|
199
199
|
`).filter(Boolean).slice(-t).join(`
|
|
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}
|
|
200
|
+
`)}catch{return""}}async function Hj(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await Fj(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await mT(e.slug)||"server exited before listening"),er.delete(e.id),await tr(e,e.state,e.error?{error:e.error}:{}),!0}if(await Fi(e.port))return er.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await tr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>Uj?(e.state="error",e.error=await mT(e.slug)||"never started listening",await tr(e,"error",{error:e.error}),!0):!1;let o=(er.get(e.id)||0)+1;return er.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Bu(){if(!lh){lh=!0;try{let e=await nr(),t=!1;for(let n of e.views)await Hj(n)&&(t=!0);t&&await En()}finally{lh=!1}}}async function ju({trackId:e}={}){await Bu();let t=await nr();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function ST(e){let n=(await nr()).views.find(r=>r.id===e);return n?{...n}:null}function yh(e){return Tn&&Tn.views.find(t=>t.slug===e)||null}async function bh({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??"."),d=i==="detected"?null:async()=>{await Dj(r,u),await Bj(u,n)},f=await nr();if(i!=="detected"){let w=fh(t),g=String(n).trim(),k=f.views.find(x=>x.trackId===e&&(x.slug===w||fh(x.name)===w||x.cmd===g&&At.resolve(x.serveCwd??x.cwd)===u));if(k)return Wj(f,k,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(w=>w.state==="live"||w.state==="starting").length>=Wr)throw new Error(`view cap reached (${Wr} live) \u2014 stop one first`);let h={id:kj().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:jj(f,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Lj(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await gh(h),f.views.push(h),await En(),await tr(h,"created",{origin:i}),dh(e,h.id),i==="detected"&&await Fi(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await En(),await tr(h,"live")),{...h}}async function Wj(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 Nj(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Fi(t.port))return await En(),dh(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(d=>d.id!==t.id&&(d.state==="live"||d.state==="starting")).length>=Wr)throw new Error(`view cap reached (${Wr} live) \u2014 stop one first`);return t.origin==="detected"?(await wh(t.port),t.origin="agent"):await Ui(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),er.delete(t.id),await gh(t),await En(),await tr(t,"created",{restarted:!0,reused:!0}),dh(t.trackId,t.id),{...t,reused:!0}}async function TT(e){let n=(await nr()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await wh(n.port):await Ui(n.slug),n.state!=="stopped"&&(n.state="stopped",er.delete(n.id),await En(),await tr(n,"stopped")),{...n}}async function AT(e){let t=await nr(),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>=Wr)throw new Error(`view cap reached (${Wr} live) \u2014 stop one first`);return await Ui(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),er.delete(n.id),await gh(n),await En(),await tr(n,"created",{restarted:!0}),{...n}}async function ET(e){let t=await nr(),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 wh(r.port):await Ui(r.slug),t.views.splice(n,1),er.delete(r.id),await En(),await tr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function _T(e){let t=await nr(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Ui(r.slug),er.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await En()),n.length}async function IT(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await nr(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await En()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(qj)){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(!vT().has(c)&&!n.views.some(l=>l.port===c)&&await Fi(c))try{i.push(await bh({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 An,Sj,hT,dT,fT,Wr,Tj,Xs,pT,Bi,Aj,uh,Ej,_j,Ij,ph,wT,ih,Oj,Mj,yT,Pu,Tn,ch,ji,mh,bT,er,Uj,lh,qj,Lu=U(()=>{ut();me();wr();An=vj(xj),Sj=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||yj.tmpdir(),".bostrat"),hT=At.join(Sj,"views.json"),[dT,fT]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Wr=Number(process.env.BOSTRAT_VIEW_MAX)||3,Tj="bostrat-view-",Xs=e=>`${Tj}${e}`,pT=new Set(["bostrat","www","api","views","app"]),Bi=e=>Object.assign(new Error(e),{status:400}),Aj=new Set(["npm","pnpm","yarn","bun"]),uh="package.json",Ej=/[|;&`]|\$\(/,_j=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Ij=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,ph=e=>String(e).trim().split(/\s+/).filter(Boolean),wT=/^[A-Za-z_][A-Za-z0-9_]*=/;ih=e=>e==="/",Oj=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");Mj=e=>Li.stat(e).then(t=>t.isDirectory(),()=>!1),yT=e=>Li.stat(e).then(t=>t.isFile(),()=>!1),Pu=e=>Li.realpath(e).catch(()=>At.resolve(e));Tn=null,ch=null;ji=new Map,mh=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,bT=12e3;er=new Map,Uj=12e4;lh=!1;qj=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Fu(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>Gj?null:n}function Vj(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Uu(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await H(e)}catch{return}let o=Fu(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await Ro(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(kh.get(e.id)||0)<Kj)return;kh.set(e.id,a);let i;try{i=await vn(e.cwd,{caller:"reconcile"})}catch{return}let c=await Gs(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await _e(e.cwd).catch(()=>null);if(u){let{link:d,created:f}=await Rt({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!f&&!(d?.trackId===e.id&&await Kn(u,l.number))){if(!Vj(d,o))return;let p=await H(e).catch(()=>null);if(p&&Fu(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(()=>{}),kh.delete(e.id)}var Kj,kh,Gj,Hu=U(()=>{me();$o();mr();Vn();$i();Oo();Kj=6e3,kh=new Map,Gj=30*6e4});import xh from"node:os";import{execFile as zj}from"node:child_process";import{promisify as Yj}from"node:util";async function Wu(){if(process.platform!=="darwin")return xh.freemem();try{let{stdout:e}=await Jj("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:xh.freemem()}catch{return xh.freemem()}}var Jj,vh=U(()=>{Jj=Yj(zj)});import Ye from"node:fs/promises";import Xe from"node:path";import CT from"node:os";import{randomUUID as Xj}from"node:crypto";import{execFile as Zj}from"node:child_process";import{promisify as Qj}from"node:util";import{chromium as eL}from"playwright-core";async function $n(){return No||(Sh||=(async()=>{try{No=JSON.parse(await Ye.readFile(NT,"utf8"))}catch{No={mocks:[]}}finally{Sh=null}return No.mocks||=[],No})(),Sh)}async function qt(){await St(NT,No)}function jT(e,t){In(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&In(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 Kt(e,t){let n=Wi(e.cwd);return{html:Xe.join(n,`${e.id}.v${t}.html`),png:Xe.join(n,`${e.id}.v${t}.png`)}}function Hi(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?Xe.join(e.cwd,r):null}function an(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 Eh(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function qu(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(iL)}function cL(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 lL(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 uL({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 dL(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,d=s,f=-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>f&&(f=g),h<d&&(d=h),p=h);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},t.toString("base64"))}async function fL(e,t,{bg:n,crop:r}){let o=await e.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function pL({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await eL.launch({executablePath:PT,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:Zs})).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 dL(a,i),u=l&&uL({...l,pad:rL*Zs,align:Zs});u&&(i=await fL(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<DT)throw new Ot(`capture produced a ${i.length}-byte image`);let c=Eh(i);if(!c)throw new Ot("output is not a PNG");return await Ye.writeFile(t,i),{width:Math.round(c.width/Zs),height:Math.round(c.height/Zs),dpr:Zs,renderMs:Date.now()-r}}catch(s){throw s instanceof Ot?s:new Ot(/timeout/i.test(s?.message||"")?`render timed out after ${Qs}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function OT({htmlPath:e,outPath:t,width:n}){let r=await Ye.mkdtemp(Xe.join(CT.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Ah($T,["--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 Ot(`firefox not found (${$T})`,""):new Ot(c?.killed?`render timed out after ${Qs}ms`:"firefox exited non-zero",s)}let a=await Ye.stat(t).catch(()=>null);if(!a||a.size<DT)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=Eh(await lL(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 mL(e){if(tL==="firefox")return OT(e);try{return await pL(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await OT(e)}catch(n){throw/not found/.test(n?.message||"")?new Ot(`no browser installed \u2014 chromium missing at ${PT} 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 LT(e){return Th=Th.then(e,e),Th}async function FT(e,t){let n=await $n(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await qt(),await yt(r,"rendering",{version:t});try{let o=await Wu();if(o<oL)throw new Ot(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Kt(r,t),a=await hL({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}_h(r,{v:t,source:"html",htmlRel:Xe.relative(r.cwd,s.html),pngRel:Xe.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await qt(),await yt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),_n(r),In(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 yt(r,"error",{version:t,error:r.error}),_n(r)}}}function _h(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 wL({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?nL:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function qi({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 rr(e){let n=(await $n()).mocks.find(r=>r.id===e);return n?{...n}:null}function UT({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&&!qu(t))throw new Error("image must be a PNG");if(n){let o=cL(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>RT)throw new Error(`video must be ${RT/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function HT({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=wL({trackId:e,name:t,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=UT({html:n,image:r,video:o}),h=await $n(),w={id:Xj().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:f?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Ye.mkdir(Wi(s),{recursive:!0}),h.mocks.push(w),f?(await Ye.writeFile(Kt(w,1).html,n),await qt(),await yt(w,"created"),_n(w),In(w,1,"html"),LT(()=>FT(w.id,1))):o?(await qT(w,1,o,p),await qt(),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}),_n(w),jT(w,w.versions[0])):(await KT(w,1,r),await qt(),await yt(w,"created"),await yt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),_n(w),In(w,1,"png")),{...w}}async function WT(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=UT({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(Wi(a.cwd),{recursive:!0}),i?(a.state="queued",await Ye.writeFile(Kt(a,l).html,t),await qt(),await yt(a,"updated",{version:l}),_n(a),In(a,l,"html"),LT(()=>FT(a.id,l))):r?(await qT(a,l,r,c),await qt(),await yt(a,"updated",{version:l}),await yt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:an(a)?.durationS??null}),_n(a),jT(a,a.versions.find(u=>u.v===l))):(await KT(a,l,n),await qt(),await yt(a,"updated",{version:l}),await yt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),_n(a),In(a,l,"png")),{...a}}async function gL(e){try{let{stdout:t}=await Ah(sL,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:BT,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 yL(e,t){try{await Ah(aL,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:BT,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Ye.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function qT(e,t,n,r){let o=Wi(e.cwd),s=Xe.join(o,`${e.id}.v${t}.${r.ext}`),a=Xe.join(o,`${e.id}.v${t}.poster.png`);await Ye.writeFile(s,n);let i=await gL(s),c=await yL(s,a);_h(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Xe.relative(e.cwd,s),posterRel:c?Xe.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 KT(e,t,n){let r=Eh(n);if(!r)throw new Error("image must be a PNG");let o=Kt(e,t);await Ye.writeFile(o.png,n),_h(e,{v:t,source:"image",htmlRel:null,pngRel:Xe.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function GT(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 yt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function VT(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=Kt(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(Xe.join(r.cwd,s),{force:!0}).catch(()=>{});return await qt(),await yt(r,"deleted"),Zt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function zT(e){let n=(await $n()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Xe.join(MT,"mock-archive",e.id),o=Wi(e.cwd),s=Xe.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(Xe.join(o,i),Xe.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(Xe.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(),_n(i)}return await qt(),n.length}async function YT(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)Zt("mocks",r.id).catch(()=>{})}return n.length}async function JT(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await $n();for(let r of n.mocks){await _n(r);for(let o of r.versions||[])o.htmlRel&&await In(r,o.v,"html"),(o.pngRel||o.posterRel)&&await In(r,o.v,"png"),o.videoRel&&await In(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Ah,MT,NT,tL,PT,$T,nL,Zs,rL,DT,Qs,oL,RT,sL,aL,BT,Ot,No,Sh,_n,In,Wi,iL,hL,Th,ea=U(()=>{me();wr();vh();yr();Ah=Qj(Zj),MT=process.env.BOSTRAT_STATE_DIR||Xe.join(process.env.HOME||CT.tmpdir(),".bostrat"),NT=Xe.join(MT,"mocks.json"),tL=process.env.BOSTRAT_MOCK_ENGINE||"chromium",PT=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",$T=process.env.BOSTRAT_FIREFOX_BIN||"firefox",nL=1300,Zs=2,rL=32,DT=1500,Qs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,oL=700*1024*1024,RT=100*1024*1024,sL=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",aL=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",BT=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"}},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
|
|
205
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
203
|
+
`)}`:t),this.name="MockRenderError"}},No=null,Sh=null;_n=e=>wt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),In=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Hi(e,r,"video"):r&&Hi(e,r,n)||(n==="html"?Kt(e,t).html:null);return o?gr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Wi=e=>Xe.join(e,".bostrat","mocks");iL=Buffer.from([137,80,78,71,13,10,26,10]);hL=mL,Th=Promise.resolve()});import bL from"node:fs/promises";import tA from"node:path";import kL from"node:os";import{randomUUID as nA}from"node:crypto";async function sr(){return or||(Ih||=(async()=>{let e=!0;try{or=JSON.parse(await bL.readFile(rA,"utf8"))}catch{or={flows:[]},e=!1}finally{Ih=null}if(or.flows||=[],!e&&je()){let{records:t,source:n}=await ot("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&or.flows.push(r.data);or.flows.length&&await ta().catch(()=>{})}}return or})(),Ih)}async function ta(){await St(rA,or)}function $h(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 oA({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>XT)throw new Error(`steps must be an array of 1-${XT}`);let r=await qi({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 d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>eA)throw new Error(`${l}: title over ${eA} chars`);let f=String(c.mock??"").trim().replace(/^mock:/,"");if(!f)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(x=>x.id===f);if(p.length||(p=r.filter(x=>x.name.toLowerCase()===f.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${f}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${f}" is ambiguous (${p.map(x=>`mock:${x.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(x=>x.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let g=o.findIndex(x=>x.mockId===h.id),k=g>=0?o.splice(g,1)[0].id:nA().slice(0,8);s.push({id:k,mockId:h.id,caption:u,title:d,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function vL(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>ZT)throw new Error(`name over ${ZT} chars`);return e.trim()}async function sA({trackId:e}={}){let t=await sr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function SL(e){let n=(await sr()).flows.find(r=>r.id===e);return n?{...n}:null}async function aA(e){let t=await SL(e);if(!t)return null;let n=new Map((await qi({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=an(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 iA({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=vL(t),i=await sr(),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 oA({trackId:e,steps:n}),d=Date.now(),f={id:nA().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:d}],steps:l,createdAt:d,updatedAt:d};return i.flows.push(f),await ta(),await $h(f,"created"),Ku(f),{...f}}async function cA(e,{steps:t,note:n}={}){let o=(await sr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await oA({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 ta(),await $h(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Ku(o),{...o}}async function lA(e){let t=await sr(),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 ta(),await $h(r,"deleted"),Zt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function uA(e){return(await sr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function dA(e){let n=(await sr()).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(),Ku(r);return n.length&&await ta(),n.length}async function fA(e){let t=await sr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await ta();for(let r of n)Zt("flows",r.id).catch(()=>{})}return n.length}async function pA(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await sr();for(let r of n.flows)await Ku(r);return{pushed:n.flows.length,source:"plane"}}var xL,rA,XT,ZT,QT,eA,or,Ih,Ku,Rh=U(()=>{me();wr();ea();yr();xL=process.env.BOSTRAT_STATE_DIR||tA.join(process.env.HOME||kL.tmpdir(),".bostrat"),rA=tA.join(xL,"flows.json"),XT=20,ZT=80,QT=500,eA=80,or=null,Ih=null;Ku=e=>wt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import cr from"node:fs/promises";import lr from"node:path";import TL from"node:os";import{randomUUID as AL}from"node:crypto";function Ki(e){return EL.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Rn(){return ar||(Oh||=(async()=>{let e=!0;try{ar=JSON.parse(await cr.readFile(hA,"utf8"))}catch{ar={runbooks:[]},e=!1}finally{Oh=null}return ar.runbooks||=[],!e&&je()&&await _L().catch(()=>{}),ar})(),Oh)}async function Do(){await St(hA,ar)}function Vu(e){return Gi(wt("runbooks",{...e},{trackId:e.trackId}))}async function _L(){let{records:e,source:t}=await ot("runbooks");if(!(t!=="plane"||!e.length)){await cr.mkdir(Gu,{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 Ha("runbooks",r.id,o.v,"md");typeof s=="string"&&await cr.writeFile(ir(r,o.v),s).catch(()=>{})}ar.runbooks.push(r)}ar.runbooks.length&&await Do()}}function na(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 ir(e,t){return lr.join(Gu,`${e.id}.v${t}.md`)}function wA(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 gA(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>mA)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${mA/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ki(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 IL(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function $L({trackId:e,status:t}={}){let n=await Rn(),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 yA({trackId:e,status:t}={}){let n=await Rn(),r=await $L({trackId:e,status:t});if(!je())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await ot("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 Ch(e){let t=await RL(e);if(t)return t;if(!je())return null;let{records:n,source:r}=await ot("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 bA(e,t){let n=await Ha("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function RL(e){let n=(await Rn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function kA({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=gA(r),c=await Rn(),l={id:AL().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 cr.mkdir(Gu,{recursive:!0}),await cr.writeFile(ir(l,1),r),l.versions.push({v:1,mdRel:lr.relative(Po,ir(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Do(),await na(l,"created",{version:1}),Vu(l),Gi(gr("runbooks",l.id,1,"md",ir(l,1))),{...l}}async function xA(e,{md:t,note:n}={}){let o=(await Rn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");IL(o,"update");let s=gA(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await cr.mkdir(Gu,{recursive:!0}),await cr.writeFile(ir(o,a),t),o.versions.push({v:a,mdRel:lr.relative(Po,ir(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Do(),await na(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Vu(o),Gi(gr("runbooks",o.id,a,"md",ir(o,a))),{...o}}async function vA(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Rn()).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 Do(),await na(r,t==="done"?"done":"reopened"),Vu(r),{...r})}async function SA(e){let t=await Rn(),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 cr.rm(lr.join(Po,o.mdRel),{force:!0}).catch(()=>{});return await Do(),r.trackDeletedAt||await na(r,"deleted"),Gi(Zt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function TA(e){let n=(await Rn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await na(r,"orphaned"),r.cwd=null,Vu(r);return n.length&&await Do(),n.length}async function AA(e){let t=await Rn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await na(r,"deleted");for(let o of r.versions||[])await cr.rm(lr.join(Po,o.mdRel),{force:!0}).catch(()=>{});Gi(Zt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Do()),n.length}async function EA(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Rn();for(let r of n.runbooks){await wt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await gr("runbooks",r.id,o.v,"md",lr.join(Po,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Po,hA,Gu,mA,EL,ar,Oh,Gi,zu=U(()=>{wr();me();yr();Po=process.env.BOSTRAT_STATE_DIR||lr.join(process.env.HOME||TL.tmpdir(),".bostrat"),hA=lr.join(Po,"runbooks.json"),Gu=lr.join(Po,"runbooks"),mA=256*1024,EL=[{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/}];ar=null,Oh=null;Gi=e=>Promise.resolve(e).catch(()=>{})});import Mh from"node:fs/promises";import Vi from"node:path";import{randomUUID as OL}from"node:crypto";function Ph(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return CL.has(t)?"image":ML.has(t)?"doc":null}function NL(e){let r=Vi.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 $A(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>_A)throw new Error(`file too large \u2014 max ${Math.floor(_A/(1024*1024))} MB`);let r=NL(t),o=Vi.extname(r).slice(1),s=Ph(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=OL().slice(0,8),i=Nh(e);await Mh.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Mh.writeFile(Vi.join(i,c),n,{mode:384}),await Mh.chmod(Vi.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${IA}/${c}`,kind:s,size:n.length}}function RA(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Dh(e){return e.role==="user"&&PL.test((e.text||"").trim())}var IA,Nh,_A,CL,ML,PL,Bh=U(()=>{IA=".bostrat/uploads",Nh=e=>Vi.join(e,IA),_A=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,CL=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),ML=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"]);PL=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Bo(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var OA=U(()=>{});import{execFile as DL}from"node:child_process";import{promisify as BL}from"node:util";function jh(e){return`refs/bostrat/wip/${e}`}function FL(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function NA(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function UL(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:LL}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 qr(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await qr(o,["remote","get-url","origin"])}catch{return CA.has(o)||(CA.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 Ps(o,"bostrat: wip snapshot"),{stdout:a}=await qr(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&Yu.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=jh(r);return await qr(o,["push","--force","origin",`${s}:${c}`],MA),Yu.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 Fh(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await qr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await qr(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await Ps(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await qr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Yu.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=FL(n);return await qr(r,["push","origin",`${o}:${i}`],MA),Yu.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 PA({listTracks:e,sweepMs:t=UL(),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 Lh(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 jL,LL,MA,qr,Yu,CA,Uh=U(()=>{zl();me();xn();jL=BL(DL),LL=12e4,MA=6e4;qr=(e,t,n=15e3)=>jL("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Yu=new Map,CA=new Set});import HL from"node:fs";import Hh from"node:fs/promises";import WL from"node:path";function VL(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function DA(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(!VL(t))return{restored:!1,reason:"plane not configured"};let s=ls(e),a=WL.join(s,`${o}.ndjson`);try{if(HL.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=Re(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let f=new AbortController,p=setTimeout(()=>f.abort(),GL),h;try{h=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${qL}`,{headers:c,signal:f.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>KL)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 Hh.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await Hh.writeFile(d,l.join(`
|
|
206
206
|
`)+`
|
|
207
|
-
`),await
|
|
207
|
+
`),await Hh.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var qL,KL,GL,BA=U(()=>{lt();me();qL=1e3,KL=5e5,GL=3e4});function jA({archivedAt:e=null,archiving:t=!1,prompted:n=!1,openActions:r=0,openActionSince:o=null,loopAttention:s=!1,operation:a=null,phase:i=null,working:c=!1,workingSince:l=null,waiting:u=null,completedTurnAt:d=null,live:f=!1,now:p=Date.now()}={}){if(e&&!t)return Gt("archived","lifecycle",e);if(n||r>0||s)return Gt("needs-you",n?"pane":s?"loop":"obligations",o||p,{count:r||(n||s?1:null)});if(t)return Gt("archiving","lifecycle",e||p);if(a?.kind==="merge")return Gt("merging","operation",a.startedAt,{count:a.count??null,summary:a.summary??null});if(a?.kind==="reconcile")return Gt("reconciling","operation",a.startedAt,{summary:a.summary??null});if(a?.kind==="fix-ci")return Gt("fixing-ci","operation",a.startedAt,{summary:a.summary??null});if(a?.kind==="rebase")return Gt("rebasing","operation",a.startedAt,{summary:a.summary??null});if((c||n)&&i?.phase){let h={reconcile:"reconciling","fix-ci":"fixing-ci","fixing-ci":"fixing-ci",rebase:"rebasing",merge:"merging"}[i.phase];if(h)return Gt(h,"agent-phase",i.at,{summary:i.summary??null})}return c?Gt("working","pane",l||p):u?Gt("waiting",u.source||"operation",u.since,{summary:u.summary??null,count:u.count??null}):d&&f?Gt("settled","obligations",d,{summary:"no open actions"}):Gt("idle","fallback",d||p)}var Gt,LA=U(()=>{Gt=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r})});async function FA(e){if(Ju.has(e.id))return Ju.get(e.id);let t=await H(e).catch(()=>[]),n=[];try{n=bn(t)}catch{}let r=Bl(n),o=n.filter(f=>f?.kind==="followup-offer"&&f.status==="open"),s=new Set(t.filter(f=>f.type==="prompt.answered").map(f=>f.payload?.promptId)),a=t.filter(f=>f.type==="prompt.raised"&&f.payload?.promptId&&!s.has(f.payload.promptId)),i=[...r,...o,...a.map(f=>({ts:f.payload?.ts??f.ts}))],c=null;for(let f of t){if(f.type!=="track.phase.changed")continue;let p=f.payload||{};c=p.phase?{phase:p.phase,summary:p.summary??null,scope:p.scope??null,phaseId:p.phaseId??null,at:p.ts??f.ts}:null}let l=t.reduce((f,p)=>p.type==="turn.agent"?Math.max(f,p.payload?.ts??p.ts??0):f,0)||null,u=t.reduce((f,p)=>p.type==="turn.user"?Math.max(f,p.payload?.ts??p.ts??0):f,0)||null,d={openActions:i.length,openActionSince:i.length?Math.min(...i.map(f=>f.ts||Date.now())):null,phase:c,completedTurnAt:l,currentTurnAt:u};return Ju.set(e.id,d),d}var Ju,UA=U(()=>{me();jl();Ju=new Map;rt.on("event",e=>{e?.trackId&&Ju.delete(e.trackId)})});import{randomUUID as zL}from"node:crypto";async function Wh(e,t,n={}){let r={operationId:zL(),kind:t,startedAt:Date.now(),...n};return Xu.set(e.id,r),await $(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:r.startedAt,state:"started",...r}}).catch(()=>{}),r}async function qh(e,t,n="completed",r=null){Xu.get(e.id)?.operationId===t?.operationId&&Xu.delete(e.id),await $(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:Date.now(),state:"finished",operationId:t?.operationId,kind:t?.kind,outcome:n,...r?{error:String(r)}:{}}}).catch(()=>{})}var Xu,HA,Kh=U(()=>{me();Xu=new Map;HA=e=>Xu.get(e)||null});var kE={};lf(kE,{HANDOFF_LATEST_REL:()=>jo,KNOWN_AGENTS:()=>oc,TRACK_NAME_MAX:()=>ow,addTrackRepo:()=>kw,adoptTrack:()=>jw,agentAvailability:()=>td,announceNodeUpdate:()=>Cn,answerFullscreenOffer:()=>Ow,answerPrompt:()=>rd,archiveTrack:()=>Ho,archiveTrackForMergedPr:()=>Xn,augmentFirstPromptAfterHandoff:()=>ZA,buildAgentCmd:()=>iw,canResumeSubtask:()=>gE,cancelQueued:()=>ic,cancelQueuedToDraft:()=>pw,cancelStopJob:()=>cc,cancelSubtask:()=>pd,changedRepos:()=>Mo,commitTransferOut:()=>dd,createSetupTrack:()=>Sw,createTrack:()=>ad,deleteTrack:()=>Aw,demoteTransferredTracks:()=>Bw,ensureLeadEpochs:()=>ca,forceMergeSubtask:()=>Hw,getHandoffJob:()=>fa,getSubtaskJob:()=>ZF,getTrack:()=>se,getTransferOutJob:()=>Nw,healDeadLead:()=>nc,interruptLead:()=>sd,isArchiving:()=>FF,isHandoffNoteEcho:()=>ew,isInterruptEcho:()=>tw,isModelSwitchEcho:()=>nw,leadCliDead:()=>_w,listSubtaskJobs:()=>qo,listTracks:()=>pe,matchTranscriptsToBoxes:()=>tE,mergeNotifyMessage:()=>pE,navigatePrompt:()=>fw,notifyTrackForMergedPr:()=>Zn,notifyTrackForResolvedPr:()=>id,offerFollowupsForMergedPr:()=>Wo,pendingOffers:()=>uw,queueShipCompleteNotify:()=>hw,queuedMessages:()=>Lo,reapArchivedOrphans:()=>Rw,reconcileDemotedTracks:()=>$w,reconcileHandoffOutcome:()=>mE,reconcileInterruptedHandoffs:()=>Mw,reconcileInterruptedSubtasks:()=>Lw,reconcileNodeUpdateNotices:()=>lw,reconcileSubtaskOutcome:()=>wE,refreshStandingInstructions:()=>vw,refuseIfTransferred:()=>Dw,rehydrateLead:()=>uc,removeTrackRepo:()=>xw,renameTrack:()=>Iw,reposOf:()=>Pt,resolveTrackBox:()=>Di,resolveTrackBoxByHookToken:()=>ac,restartLead:()=>ld,resumeAgentCmd:()=>cw,sayToLead:()=>Nt,sendOptionsForAgent:()=>yn,setTrackDraft:()=>yw,setTrackModel:()=>bw,startLeadHandoff:()=>ud,startSpinUpView:()=>ww,startSubtask:()=>Uw,startTransferOut:()=>Pw,stopJobActive:()=>oE,subtaskMessages:()=>dw,trackMessages:()=>nd,unarchiveTrack:()=>cd});import Q from"node:fs/promises";import te from"node:path";import{randomUUID as Uo}from"node:crypto";import{execFile as YL}from"node:child_process";import{promisify as JL}from"node:util";function iw(e,{skipPermissions:t=!0}={}){let n=ed[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 cw(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=ed[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&&!tn(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 da(e){let t=ed[e];if(t)return process.env[t.envVar]||t.base}async function td({maxAgeMs:e=6e4}={}){if(Kr.value&&Date.now()-Kr.at<e)return Kr.value;let t=Kr.value,n=await Promise.all(oc.map(async r=>{let o=await wl(da(r),{retries:[]});return o===hs?[r,t?.[r]??!0]:[r,o===hl]}));return Kr.value=Object.fromEntries(n),Kr.at=Date.now(),Kr.value}async function sc(e,t=!0){let n=iw(e,{skipPermissions:t});if(await wl(n)===ms){Kr.at=0;let r=n.split(/\s+/)[0],o=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":Ff[r]?`Install it on this node: ${Ff[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 ZA(e,t=jo){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 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()}
|
|
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 ew(e){return e.role==="user"&&QL.test((e.text||"").trim())}async function ne(){return Vt||(Gh||=(async()=>{try{Vt=JSON.parse(await Q.readFile(JA,"utf8"))}catch{Vt={tracks:[]}}finally{Gh=null}return Vt})(),Gh)}async function ie(){await St(JA,Vt)}function nF(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
210
|
+
`;return Vh=Vh.then(()=>Q.mkdir(On,{recursive:!0})).then(()=>Q.appendFile(XL,n)).catch(()=>{}),Vh}async function Cn({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await Q.readFile(zh,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await ne(),u=0;for(let d of l.tracks)Ge(d)||!d.leadBox||(Ie(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ie().catch(()=>{}),s==="available")try{await Q.mkdir(te.dirname(zh),{recursive:!0}),await Q.writeFile(zh,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function lw({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)||rF(e,c)||(Ie(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 ie().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function rF(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 QA(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=>oF.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 eE(e,t,n,{kind:r}={}){let o=await xe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=da(o.agent);await Sr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await nt(t,n,{...yn(o.agent,n),kind:r||"orchestration"});let a=await li({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await ce({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);Ie(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await ie()}async function sF(e){let t=te.join(ls(e),`${e.id}.ndjson`),n;try{n=await Q.stat(t)}catch{return null}let r=WA.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await H(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return WA.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function aF(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Rm(n.name,n.agent)}async function Ct(e,t=null,n=null){let r=t??new Map((await Qt()).map(y=>[y.name,y])),o=!!e.transferredTo,s=e.boxes.map(y=>{let _=o?null:r.get(y.name),M=Gr.has(y.state)?y.state:_?y.role==="lead"?"live":y.state:"stopped";return{...y,state:M,cwd:_?.cwd||y.cwd,agent:_?.agent||y.agent,sessionId:_?.sessionId||y.sessionId}}),[a,{working:i,prompted:c},l,u]=await Promise.all([sF(e),aF(e,s),FA(e),n??Gn().catch(()=>[])]),d=tc.has(e.id),f=(u||[]).filter(y=>y.trackId===e.id&&!["merged","closed","lost"].includes(y.state)),p=f.find(y=>y.reconcile||y.ciFix),h=HA(e.id)||(p?.reconcile?{kind:"reconcile",startedAt:p.reconcile.delegatedAt}:p?.ciFix?{kind:"fix-ci",startedAt:p.ciFix.delegatedAt}:null),w=f.filter(y=>y.ciFixAwait||y.reconcileAwait||y.autoMerge),g=w.length?{since:Math.min(...w.map(y=>y.ciFixAwait?.since||y.reconcileAwait?.since||y.armedAt||Date.now())),count:w.length,summary:w.some(y=>y.ciFixAwait)?"waiting for CI":w.some(y=>y.reconcileAwait)?"waiting for mergeability":"auto-merge armed"}:null,k=s.find(y=>y.name===e.leadBox)?.state==="live",x=jA({archivedAt:e.archivedAt,archiving:d,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,operation:h,phase:l.phase,working:i,workingSince:l.currentTurnAt,waiting:g,completedTurnAt:l.completedTurnAt||a,live:k});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:d,status:x}}function tE(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-iF,f=r[u+1]?.createdAt??1/0,p=o.find(h=>!s.has(h.id)&&h.birthMs>=d&&h.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function nE(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(f=>f.box.sessionId),a=Ub(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=tE(u,a,f=>i.has(f));for(let f of d){let p=u.find(h=>h.name===f.box);p&&(p.sessionId=f.sessionId),await Cb(f.box,f.sessionId),n=!0}}return n}async function pe(){let e=await ne();await nE(e)&&await ie();let t=new Map((await Qt()).map(r=>[r.name,r])),n=await Gn().catch(()=>[]);return Promise.all(e.tracks.map(r=>Ct(r,t,n)))}async function Di(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 ac(e){let t=$a(e);if(!t)return null;let n=await ne();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&ky(o.hookTokenHash,t))return{track:r,box:o};return null}async function se(e){let t=await ne(),n=ye(t,e);return n?(await nE(t)&&await ie(),Ct(n)):null}async function uw(e){let t=await H(e).catch(()=>[]),n;try{n=bn(t)}catch{return[]}return Bl(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function tw(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(cF[e.agent]||[]).some(n=>n.test(t))}function nw(e){let t=(e.text||"").trim();return t?lF.some(n=>n.test(t)):!1}async function nd(e,t={}){let n=await se(e);if(!n)return null;let r=new Set([...n.events.filter(E=>E.type==="orchestration-prompt"&&E.prompt).map(E=>String(E.prompt).trim()),...n.events.filter(E=>E.type==="said"&&E.text).map(E=>String(E.text).trim())]),o=fa(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=hh(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...xT(n),...l],f={windows:[...k0(n),...x0(n,Ul),...a,...u]},h=(await Promise.all(n.boxes.map(E=>Wp(E,t)))).flat(),w=h.map(E=>{let P={kind:"message",...E};if(P.role==="assistant"&&(P.text||"").trim()&&P.tools?.length){let he=wi(P.agent).cliTools,Ze=P.tools.filter(Be=>!he.has(Be));return{...P,tools:Ze.length?Ze:void 0}}return P}).map(E=>{if(E.role!=="user")return E;let P=_s(E.text);return P?P.kind==="suppress"?null:{...E,note:P,text:Wk(P)}:E}).filter(Boolean).filter(E=>!(E.role==="user"&&r.has((E.text||"").trim()))).filter(E=>!tw(E)).filter(E=>!nw(E)).filter(E=>!ew(E)).filter(E=>!Dh(E)).filter(E=>E.role!=="assistant"||!!(E.text||"").trim()||!!E.images?.length).filter(E=>!v0(E,f)),g=w.filter(E=>E.failed&&E.text===fo);if(g.length){let E=new Map;for(let P of g){if(!E.has(P.box)){let Be=[...(await Pe(P.box,{lines:tt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Et=>Et[1]);E.set(P.box,Be.length?Be[Be.length-1]:null)}let he=E.get(P.box);he&&(P.text=`\u25A0 ${he}`)}}let k=await Promise.all(n.boxes.map(E=>tx(E,t)));await rT(n,k.flat());let{agentTurnBoxes:x}=await tT(n,w);for(let E of n.boxes)x.has(E.name)&&E.cwd&&sl(E.cwd);await Jx(n),await IT(n,w).catch(()=>{});for(let E of n.boxes)E.agent==="codex"&&E.state==="live"&&await sT(n,E),E.agent==="cursor"&&E.state==="live"&&await uT(n,E);let y=await Promise.all(n.boxes.map(E=>ex(E,t)));await nT(n,y.flat().filter(E=>!Yh(E.box,E.ts,u)));let _=await H(n),M=c&&i.viewId?i.viewId:null,j=cv(_),L=bn(_).filter(E=>!(E.kind==="message"&&(tw(E)||nw(E)||ew(E)||Dh(E)))&&!(E.kind==="view-card"&&M&&E.viewId===M)),F=lv(L,j),A=L.filter(E=>!(E.kind==="message"&&E.role==="assistant"&&!F.has(E)&&Yh(E.box,E.ts,j))),I=Tp(_,A).map(E=>({kind:"command-batch",id:E.id,ts:E.pending?E.ts:(E.anchorTs??E.ts)-1,anchorTs:E.anchorTs,anchorBox:E.anchorBox,agent:E.agent,boxRole:E.boxRole,commands:E.commands,pending:E.pending})).filter(E=>!Yh(E.anchorBox,E.anchorTs??E.ts,u)),C=Jk(Xk([...A,...I].filter(E=>E.ts!=null).sort((E,P)=>E.ts-P.ts))),J=dF(n),q=J.size?C.filter(E=>E.kind==="event"||!J.has(E.box??E.anchorBox)):C,R=n.boxes.find(E=>E.name===n.leadBox)||null,W=R?await Za(R.cwd,R.sessionId,{fallbackNewest:!1}):null;if(!W&&R){let E=[..._].reverse().find(P=>P.type==="context.usage"&&P.boxId===R.name);E?.payload?.window&&(W={pct:E.payload.pct??(E.payload.occupancy!=null?E.payload.occupancy/E.payload.window:null),occupancy:E.payload.occupancy??null,window:E.payload.window,windowKnown:!0,model:E.payload.model||null})}let X=[...h].reverse().find(E=>E.role==="assistant"&&E.model),G=X?.model||null,Te=[...n.events||[]].reverse().find(E=>E.type==="model-change"),le=W?.model||G;Te&&(!X?.ts||Te.at>=X.ts)&&(le=Te.to);let be=R?await Pe(R.name,{lines:tt}).catch(()=>""):"",ae=R&&xo(R.agent,be)?await Un(R.name).catch(()=>be):be,K=R?Or(R,ae):null;if(K&&R){let E=Gl(R,K,ae);if(E){let P=gx(R.name,K.promptId);P.action!=="give-up"&&(P.action==="send"&&(We(R.name,E.keys,{kind:"auto-reject"}).catch(()=>{}),P.first&&$(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{ts:Date.now(),promptId:K.promptId,rule:E.id}}).catch(()=>{})),K=null)}}!K&&R&&!Ts(R.agent,be)&&!Lo(n.id).length&&(K=vp(q,{leadBoxId:R.name,provider:R.agent,isAnswered:E=>_.some(P=>P.type==="prompt.answered"&&P.payload?.promptId===E)})),K&&Xx(n,R,K);let ue=q.filter(E=>!(E.kind==="message"&&E.role==="assistant"&&E.ask&&!String(E.text||"").trim()&&!E.images?.length)),V=!!R&&yu(R.name,R.agent,be)&&!K;R&&!R.readyAt&&(V||K||be&&As(R.agent,be))&&(R.readyAt=Date.now(),await ie());let $e=!!R&&!R.readyAt&&Date.now()-(n.createdAt||0)<uF;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&R&&As(R.agent,be)&&!K){let E=await ne(),P=ye(E,n.id);P?.setup&&!P.setup.connectedAt&&(P.setup.connectedAt=Date.now(),n.setup.connectedAt=P.setup.connectedAt,await $(P,{type:"provider.connected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{provider:R.agent,ts:P.setup.connectedAt}}).catch(()=>{}),await ie())}let Me=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,fe=null;if(c){let E=Date.now()-i.startedAt;if(i.viewId&&!V){let P=Du(n.id);P&&(Ie(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:P.finishedAt}),await ie())}else if(K||E>bT&&!V&&!i.viewId||E>mh){let P=K?"needs-input":E>mh?"timeout":"no-view";Du(n.id,{error:P})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:R?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:P}}).catch(()=>{})}else fe={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ue,context:W?{pct:W.pct,occupancy:W.occupancy,window:W.window,windowKnown:W.windowKnown,model:W.model}:null,models:{current:le,options:Wx(R?.agent)},pendingPrompt:K,working:V,stopping:oE(n.id),leadStarting:$e,handoff:Me,view:fe,queued:Lo(n.id).filter(E=>E.kind!=="ship-notify"&&E.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function dF(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 dw(e,t){let n=await se(e);if(!n)return null;let r=qo(e).find(d=>d.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await H(n),a=bn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Tp(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await Pe(o,{lines:tt}).catch(()=>"");u=yu(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function rd(e,{promptId:t,answers:n,text:r}={}){let o=ye(await ne(),e);if(!o)throw new Error(`track "${e}" not found`);cc(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 xe(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Jp(s);if(i||(i=await fF(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 nt(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 We(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=Vk(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=px(i,n||[]);await We(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=hk(i,n||[]);await nt(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]:[]),x=[...k].map(_=>g.options?.[_]).filter(Boolean).map(_=>({label:_.label,description:_.description||""})),y=(g.options||[]).map((_,M)=>k.has(M)?null:_?.label).filter(Boolean);pm(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:x,rejected:y})}return await ce({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),d=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),f=(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:d,rejected:f,...p?{control:p}:{}},await ce({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),pm(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function fF(e,t){if(!t)return null;let n=await H(e).catch(()=>[]);return vp(bn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function fw(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 xe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Jp(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=mx(a,n);return c.length?(await We(r.leadBox,c,{kind:"navigate-prompt"}),await ce({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 od(e){let t=Xi.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Xi.set(e,t)),t}function Lo(e){return(Xi.get(e)?.items??[]).map(t=>({...t}))}function ic(e){let t=Xi.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 pw(e){let t=ic(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=ia(o,n),await ie(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function mw(e){let t=od(e);t.timer||(t.timer=setInterval(()=>mF(e).catch(()=>{}),pF))}async function mF(e){let t=Xi.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){ic(e);return}let r=fa(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Bo(t,!1);return}let o=await xe(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await _w(o)&&(o=await nc(n,s,"queue")?await xe(n.leadBox):null,await ie()),!o){Bo(t,!1);return}if(So(s?.agent)){Bo(t,!1);return}let a=await Pe(n.leadBox,{lines:tt}).catch(()=>"");if(Il(s?.agent,a)){Bo(t,!1);return}if(xo(s?.agent,a)){let c=await Un(n.leadBox).catch(()=>a);if(Or(s,c)){Bo(t,!1);return}}if(!Bo(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await rw(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 nc(n,s,"queue-gate")}await ie()}finally{t.flushing=!1}}}async function rE(e,t,n,r){let o=So(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ia(e,[n]),await ie()),Mx(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 hF(e,t){let n=await Pe(e.leadBox,{lines:tt}).catch(()=>"");if(!xo(t?.agent,n))return!1;let r=await Un(e.leadBox).catch(()=>n);return!!Or(t,r)}async function rw(e,t,n,r,o=null,s=null){await rE(e,t,r,o);let a=Zi.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=ZA(r,e.pendingHandoffHydrate.artifact||jo),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(u.length){let f=RA(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
211
211
|
|
|
212
|
-
${
|
|
212
|
+
${f}`:f}let d=Ie(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||aa(e,null);try{if(await Sr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:da(n.agent),bailOnDialog:!0}),(Zi.get(e.id)||0)!==a){!o&&r.trim()&&(ia(e,[r]),await ie());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}if(await hF(e,t)){let f=od(e.id),p={id:Uo(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};f.items.unshift(p),mw(e.id);let h=new Error("lead is blocked on a prompt \u2014 message queued");throw h.requeued={id:p.id,position:f.items.length},h}await nt(e.leadBox,i,{...yn(t?.agent,i),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),Ie(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:d.at,text:r,...f?.interrupted?{reason:"interrupted"}:{},...f?.requeued?{reason:"requeued"}:{}}),f?.requeued)return{queued:!0,...f.requeued};throw f}await $F(e,t,i)||(await sE(e.leadBox,t?.agent,i.length+8,i).catch(()=>{}),Ie(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:d.at,text:r,reason:"unsubmitted"}),o||ia(e,[r])),c&&await ce({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function hw(e,{actions:t}={}){od(e).items.push({id:Uo(),text:uv(t),ts:Date.now(),kind:"ship-notify"}),mw(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"||!Ms(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`);Dw(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=Ms(t);await rE(s,a,i,n),cc(e,"say");let c=await xe(s.leadBox);if(c&&await _w(c)?await nc(s,a,"say")?(await ie(),c=await xe(s.leadBox)):c=null:!c&&await uc(s)&&(await ie(),c=await xe(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=od(e),u=await Pe(s.leadBox,{lines:tt}).catch(()=>""),d=l.items.length>0||Il(a?.agent,u);if(!d&&xo(a?.agent,u)){let p=await Un(s.leadBox).catch(()=>u);d=!!Or(a,p)}if(d){let p={id:Uo(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),mw(e),n||aa(s,null),await ie(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let f;try{f=await rw(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await nc(s,a,"say-gate")||(await ie(),c=await xe(s.leadBox),!c))throw p;f=await rw(s,a,c,i,n,o)}return await ie(),f?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:f.position}:{ok:!0,leadBox:s.leadBox}}async function ww(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=hh(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";kT(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${qA}
|
|
213
213
|
|
|
214
|
-
What to preview: ${String(t).trim()}`:
|
|
214
|
+
What to preview: ${String(t).trim()}`:qA;try{await Nt(e,a,{kind:"spin-up-view"})}catch(i){throw Du(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function sd(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`);Zi.set(e,(Zi.get(e)||0)+1);let n=ic(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=sa.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=ia(t,r),await ie()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=xF(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Fo(wF)]);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 oE(e){let t=sa.get(e);return!!t&&!t.canceled}function cc(e,t){let n=sa.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function gw(e){cc(e,"track-ended"),Zi.delete(e),ic(e)}function xF(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}),sa.set(e,r),(async()=>{let s=ye(await ne(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await xe(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<gF&&!r.canceled;g++){await We(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of yF){await Fo(k);let x=await Pe(s.leadBox,{lines:tt}).catch(()=>"");if(!Ts(a?.agent,x)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<kF&&!r.canceled&&(await Fo(bF),!r.canceled);g++){let k=await Pe(s.leadBox,{lines:tt}).catch(()=>"");if(!Ts(a?.agent,k)){c=!0;break}await We(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=ye(await ne(),e)||s,d=u?.boxes.find(g=>g.name===u.leadBox)||a,f=null,p=null;u&&d&&i&&c&&!r.canceled&&(f=await AF(u,d).catch(()=>null),f||(p=await IF(u,d).catch(()=>null)));let h=u&&i&&!r.canceled?await SF(u).catch(()=>null):null,w=[f||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=ia(u,w),await ie()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await vF(u||s,d,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!h,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await ce({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:d?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!h}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),sa.get(e)===r&&sa.delete(e),o()}),r}async function vF(e,t,n){await $(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function SF(e){let t=Fu(await H(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 AF(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 TF){await Fo(r);let o=await Pe(e.leadBox,{lines:tt}).catch(()=>""),s=ii(t.agent,o);if(s&&$l(n.text,s))return await sE(e.leadBox,t.agent,n.text.length+8),Ie(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function IF(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>EF||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ve(r.text))return null;for(let s of _F){await Fo(s);let a;try{a=await Wp(t,{})}catch{return null}if(Tk(a,r))return null;let i=await Pe(e.leadBox,{lines:tt}).catch(()=>"");if(ii(t.agent,i))return null}return Ie(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function $F(e,t,n){return li({sessionName:e.leadBox,agent:t?.agent,agentCmd:da(t?.agent),text:n,trackId:e.id,flow:"say"})}async function sE(e,t,n,r=null){for(let o=0;o<3;o++){await gs(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await Fo(200);let s=await Pe(e,{lines:tt}).catch(()=>""),a=ii(t,s);if(!a||r&&!$l(r,a)&&!(t==="codex"&&pp(r,a)))return!0;n=a.length+8}return!1}function aa(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 ia(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?aa(e,n.join(`
|
|
215
215
|
|
|
216
|
-
`),{source:"restore"})?.text??null:null}function
|
|
216
|
+
`),{source:"restore"})?.text??null:null}function OF(e,t){let n=Ve(t);if(!n)return!1;if(Lo(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>RF)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 yw(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=Ms(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&OF(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))?aa(r,o):aa(r,`${i}
|
|
217
217
|
|
|
218
|
-
${o}`,{source:"restore"})}else
|
|
219
|
-
`);return`${
|
|
218
|
+
${o}`,{source:"restore"})}else aa(r,o);return await ie(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function bw(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 xe(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=qx(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Za(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Sr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:da(o.agent)});let u=await Kx(a,r.leadBox,i);return Ie(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ie(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function aE(e){for(let t of e){if(!t||!await Ns(t))continue;let n=await xx(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 Mo(e){let t=[];for(let n of Pt(e?.workspace)){if(!n?.dir)continue;let r=await gi(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await yi(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function iE(e){let t=e;typeof t=="string"&&!te.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await as(t)??await Xc(t));let n=await aE([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 CF(e,t,n,r){let o=`track/${Fw(t)}-${e}`,s=[];try{await Q.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await iE(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await _e(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 d=te.join(n,u),f=await Bs(c);await Ds(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await lc(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 kw(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 iE(typeof t=="string"?t.trim():t);if((o.repos??[]).some(f=>f?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await _e(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(f=>f?.name).filter(Boolean)]),c=te.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let f=2;i.has(c);f++)c=`${te.basename(s)}-${f}`;let l=te.join(o.dir,c),u=await Bs(s);await vx(s,o.branch)?await Vl(s,l,o.branch):await Ds(s,l,o.branch,u);let d={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),d],await lc(o.dir,o.repos,o.branch),Ie(r,"retagged",{action:"add",repo:a??s,name:c,workspace:cE(o)}),await ie(),d}async function xw(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 gi(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await yi(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 lc(o.dir,o.repos,o.branch),Ie(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:cE(o)}),await ie(),{removed:s.name}}async function MF(e,t,{repo:n,repos:r,cwd:o}={}){await Q.mkdir(Yi,{recursive:!0});let s=te.join(Yi,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return CF(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!te.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await as(n)??await Xc(n));let i=await aE([n,o,ZL]);if(i){let c=`track/${Fw(t)}-${e}`;try{let l=await Bs(i);await Ds(i,s,c,l),await ec(s);let u=await _e(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 rm(s),await ec(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ec(e){try{await Q.writeFile(te.join(e,"AGENTS.md"),zi,{flag:"wx"})}catch{return}await BF(e,["/AGENTS.md"])}function DF(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`${PF}
|
|
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
|
|
231
|
+
`}async function lc(e,t,n){let r=DF(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await Q.writeFile(te.join(e,o),r).catch(()=>{})}async function uE(e){let{stdout:t}=await YA("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return te.resolve(e,t.trim())}async function BF(e,t){try{let n=await uE(e);await Q.mkdir(te.dirname(n),{recursive:!0});let r=await Q.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
232
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 jF(e,t){try{let n=await uE(e),r=await Q.readFile(n,"utf8"),o=r.split(`
|
|
237
237
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
238
238
|
`).length&&await Q.writeFile(n,o.join(`
|
|
239
|
-
`))}catch{}}async function
|
|
239
|
+
`))}catch{}}async function vw(){let e={refreshed:0,removed:0,codexGlobal:null};try{await Q.mkdir(On,{recursive:!0}),await Q.writeFile(te.join(On,"CLAUDE.md"),NF).catch(()=>{}),await Q.writeFile(te.join(On,"AGENTS.md"),zi).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,Xh,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):Jh(o)&&o!==Xh&&(await Q.writeFile(r,Xh).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Yi,ua]){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&&Jh(c)&&c!==zi&&(await Q.writeFile(i,zi).catch(()=>{}),e.refreshed++);let l=te.join(a,"CLAUDE.md"),u=await Q.readFile(l,"utf8").catch(()=>null);u!==null&&Jh(u)&&(await Q.rm(l,{force:!0}).catch(()=>{}),await jF(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function dE(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 ad({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 sc(o,s),i=await ne(),c=Uo().slice(0,8),l=await MF(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(h=>(h.boxes||[]).map(w=>w.name))),d=`${e.trim()} lead`;u.has(gl(d))&&(d=`${e.trim().slice(0,50).trim()} ${c} lead`);let f=await xr(d,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0}),p={id:c,name:e.trim(),cwd:f.cwd,workspace:l,leadBox:f.name,boxes:[Cw(f,"lead","live")],events:[],createdAt:Date.now()};return Ie(p,"opened",{agent:o,box:f.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:h,base:w,baseRef:g,repo:k})=>({name:h,base:w,baseRef:g,repo:k}))}:{}}}),i.tracks.push(p),await ie(),oi({cwd:f.cwd,sessionName:f.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 ie())}}),Ct(p)}async function Sw({provider:e="claude",fresh:t=!1}={}){if(!LF.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(f=>f.name===u.leadBox)?.state==="live")return u}await Ho(r.id).catch(()=>{})}let o=(await sc(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Uo().slice(0,8),a=te.join(On,"setup",s);await Q.mkdir(a,{recursive:!0});let i=await xr(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&oi({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:[Cw(i,"lead","live")],events:[],createdAt:Date.now()};return Ie(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 ie(),Ct(c)}async function Tw(e,t,n){await Ef(t.cwd).catch(()=>{}),await _T(e).catch(()=>{});let r=la.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),la.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of qo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Ww(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 en(a.name).catch(()=>{})}async function Aw(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 zT(s),gw(e),await Tw(e,s,"track deleted"),n?await AA(e).catch(()=>{}):await TA(e).catch(()=>{}),await zx(),await dE(s),t&&await YT(e).catch(()=>{}),t?await fA(e).catch(()=>{}):await dA(s).catch(()=>{}),Ie(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await ie(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Ho(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(),gw(e),Ie(n,"archived",{boxes:n.boxes.length}),await ie(),n.kind==="setup"?(await fE(e),Ct(n)):(tc.add(e),HF(e).catch(()=>{}).finally(()=>tc.delete(e)),{...await Ct(n),archiving:!0})}async function HF(e){try{await WF(e)}catch{}await fE(e).catch(()=>{})}async function fE(e){let t=await ne(),n=ye(t,e);if(!(!n||!n.archivedAt)){await Tw(e,n,"track archived");for(let r of n.boxes)Gr.has(r.state)||(r.state="stopped");await ie()}}async function WF(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 xe(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await y0(n.leadBox,i.cwd,{timeoutMs:UF})),s="lead-cli"}catch{}if(r||(r=M0(n,await H(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 ie()}async function Xn(e,t){let n=await st(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Ew(n);if(r.length){for(let o of r)await Ao(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Ho(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Ew(e){return(await Gn().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 id(e,t,"merged")}async function Wo(e,t){let n=await st(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 Ew(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await vv(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 Ni(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 id(e,t,n="merged"){let r=await st(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 Ew(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await xv(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await mv(r.repo,r.number).catch(()=>!0))return!1;try{return await Nt(r.trackId,pE(a),{kind:"merge-notify"}),!0}catch{return await Eo(r.repo,r.number,!0).catch(()=>{}),!1}}function pE(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 ${KA[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 ${KA[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 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(
|
|
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}
|
|
242
|
+
${a}`}async function _w(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Ja()?!1:Fn(e.name)}async function nc(e,t,n){return!t||e.archivedAt||Gr.has(t.state)?!1:(await ce({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 en(t.name).catch(()=>{}),t.state="stopped",uc(e))}async function uc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=Zh.get(r.name);if(o)return o;let s=qF(e,r,{freshSession:t,reason:n}).finally(()=>Zh.delete(r.name));return Zh.set(r.name,s),s}async function qF(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Gr.has(t.state)||await xe(t.name))return!1;let o=n===!0,s=o?Uo():null,a=o?za(iw(t.agent),s,t.name):cw(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await wl(a);if(i===ms)return Ie(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===hs&&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(ca(t)))}:null,l=Date.now();if(o){let u=ca(t),d=u[u.length-1];d.endedAt==null&&(d.endedAt=l),d.endReason==null&&(d.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 xr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=$a(u.hookToken),oi({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&Ie(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),Ie(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),Ie(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function cd(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,tc.delete(e),Ie(r,"unarchived",{}),await uc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ie(),Ct(r)}async function Iw(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>ow)throw new Error(`name too long (max ${ow} 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,Ie(o,"renamed",{from:s,to:n}),await ie(),Ct(o)}async function $w(){let e=await ne(),t=0;for(let n of e.tracks||[])n.transferredTo&&Yl(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ie(),t}async function Rw(){let e=await ne(),t=new Set((await Qt()).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 en(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ie(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Gr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ie(),r}async function ld(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`);Dw(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Gr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await xe(r.leadBox)){let i=await Pe(r.leadBox,{lines:tt}).catch(()=>"");if(Il(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await en(r.leadBox),o.state="stopped"}let a=await uc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ie(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ct(r)}async function Ow(e,t,n){let r=await se(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await H(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 ld(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 GA(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return Su({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function ca(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 Qu(e,t){let n=await ne(),r=ye(n,e);r&&(r.handoff={...r.handoff||{},...t},await ie())}function mE(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 Mw(){let e=await ne(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=mE(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await xe(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=ca(l),f=d[d.length-1],p=f.agent,h=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:jo,at:u},Ie(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await ce({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 ce({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await en(i).catch(()=>{}),a&&await en(a).catch(()=>{})}return t&&await ie(),t}async function ud(e,t,n=!0,r=null){let o=la.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`);cc(e,"handoff");let a=await sc(t,n),i=So(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 xe(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:QA(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return la.set(e,l),await Qu(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),KF(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 Qu(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ce({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ce({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function KF(e,t,n,r,o){let s=async(M,j={})=>{e.step=M,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${M}`),await Qu(e.trackId,{step:M,toBox:e.toBox}),await ce({flow:"lead-handoff",phase:M,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:j.sessionName,...j})};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(M=>M.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",So(c))e.checkpoint="skipped-capped";else try{await Ep(e.fromBox,{timeoutMs:XA})}catch(M){e.checkpoint="reconstructed",await ce({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:M?.message||String(M)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await _p(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await Np(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=xk(f.launchCmd);await Sr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:da(o),launchEcho:p}).catch(M=>{if(M?.code!=="cli-not-running")throw M});let h=up(await Pe(e.fromBox,{lines:tt}).catch(()=>""),p);if(!As(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let M=cw(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Np(e.fromBox,{agentCmd:M,agent:c,resumeSessionId:l,pinned:u});let j=await ne(),L=ye(j,e.trackId);if(L){let F=L.boxes.find(A=>A.name===e.fromBox);F&&(F.state="live"),Ie(L,"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 ie()}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 k=g.boxes.find(M=>M.name===e.fromBox);if(!k)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),y=ca(k),_=y[y.length-1];_.endedAt=x,e.reason&&(_.endReason=e.reason),y.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:x,endedAt:null}),k.agent=o,k.sessionId=f.sessionId,k.pinned=f.pinned,k.state="live",k.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||jo,at:x},Ie(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:y.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ie(),await s("done",{sessionName:e.fromBox}),await Qu(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Pw(e){let t=aw.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=la.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 aw.set(e,s),GF(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 GF(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await xe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&So(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Ep(t.leadBox,{timeoutMs:XA})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await _p(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 d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await cn(u.dir,["push","origin",o],sw);let{stdout:f}=await cn(u.dir,["rev-parse","HEAD"]);d.baseSha=f.trim(),d.pushed=!0}catch(f){d.error=String(f?.message||f).split(`
|
|
243
|
+
`)[0]}s.push(d)}e.step="wip";let a=await Lh({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&&ca(l),await ie(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:d,baseSha:f})=>({name:u,repo:d,baseSha:f})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:f})=>({name:u,repo:d,error:f})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Dw(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 dd(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};gw(e),await Tw(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Yl(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Fh({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,Ie(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await ie(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function Bw(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await dd(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 VA(e,t,n){try{await Vl(e,t,n)}catch{await cn(e,["worktree","prune"]).catch(()=>{}),await Vl(e,t,n)}return t}function zF(e){if(typeof e!="string"||!VF.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function jw(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(e.branch!=null&&zF(e.branch),Qh.has(t))throw new Error("an adopt is already running for this Track");Qh.set(t,{startedAt:Date.now()});try{return await YF({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{Qh.delete(t)}}async function YF({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:d=[],createdAt:f=null,leadBox:p=null,reason:h=null,downSince:w=null}){let g=await sc(n,r),k=await ne(),x=ye(k,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await Fh({id:x.id,cwd:x.cwd}).catch(()=>{}),await dE(x),k.tracks.splice(k.tracks.indexOf(x),1),await ie()),await Q.mkdir(Yi,{recursive:!0});let y=te.join(Yi,e);await Q.rm(y,{recursive:!0,force:!0}).catch(()=>{});let _=null,M=!1,j=[];try{let L=[];if(i)for(let K of Array.isArray(c)?c:[]){let ue=typeof K=="string"?K:K?.repo;if(!ue||!/^[^/\s]+\/[^/\s]+$/.test(ue))continue;let re=await as(ue)??await Xc(ue);await cn(re,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],sw),L.push({slug:ue,base:re,name:typeof K=="object"?K?.name??null:null})}if(i&&L.length===1){let{base:K,slug:ue}=L[0];await VA(K,y,i),j.push({base:K,dir:y}),await ec(y);let re=await Bs(K);_={dir:y,base:K,branch:i,kind:"worktree",baseRef:re,repo:ue}}else if(i&&L.length>=2){await Q.mkdir(y,{recursive:!0});let K=new Set([".bostrat"]),ue=[];for(let re of L){let V=re.name||te.basename(re.base);K.has(V)&&(V=re.slug.replace("/","-"));for(let fe=2;K.has(V);fe++)V=`${te.basename(re.base)}-${fe}`;K.add(V);let $e=te.join(y,V);await VA(re.base,$e,i),j.push({base:re.base,dir:$e});let Me=await Bs(re.base);ue.push({name:V,dir:$e,base:re.base,baseRef:Me,repo:re.slug})}await lc(y,ue,i),_={dir:y,kind:"multi",branch:i,repos:ue}}else M=!0,await Q.mkdir(y,{recursive:!0}),await rm(y),await ec(y),_={dir:y,base:null,branch:null,kind:"repo"};let F=!1,A=null,I=null;if(l?.sha&&_.kind==="worktree")try{await cn(y,["fetch","origin",jh(e)],sw);let{stdout:K}=await cn(y,["rev-parse","FETCH_HEAD^"]),{stdout:ue}=await cn(y,["rev-parse","HEAD"]),re=K.trim()===ue.trim();re||await cn(y,["merge-base","--is-ancestor",ue.trim(),K.trim()]).then(()=>!0,()=>!1)&&(await cn(y,["reset","--hard",K.trim()]),I={from:ue.trim(),to:K.trim()},re=!0),re?(await cn(y,["read-tree","--reset","-u","FETCH_HEAD"]),await cn(y,["reset","-q"]),F=!0):A="snapshot does not parent the fetched branch head"}catch(K){A=String(K?.message||K).split(`
|
|
244
|
+
`)[0]}else l?.sha&&(A="wip snapshots cover single-repo worktrees only");await DA({id:e,cwd:y}).catch(()=>({restored:!1}));let C=te.join(y,jo),J=null;(await ll(y)).restored?J="checkpoint":typeof u=="string"&&u.trim()&&(await Q.mkdir(te.dirname(C),{recursive:!0}),await Q.writeFile(C,u),J="brief");let R=new Set(k.tracks.flatMap(K=>(K.boxes||[]).map(ue=>ue.name))),W=null;p&&!R.has(p)&&!await xe(p).catch(()=>null)?W=p:(W=`${t.trim()} lead`,R.has(gl(W))&&(W=`${t.trim().slice(0,50).trim()} ${e} lead`));let X=await xr(W,y,{agentCmd:g,agent:n});oi({cwd:X.cwd,sessionName:X.name,agent:n,agentCmd:g});let G=Date.now(),Te=Cw(X,"lead","live"),le=(Array.isArray(d)?d:[]).filter(K=>K&&typeof K=="object").map(K=>({...K})),be=le[le.length-1];be&&be.endedAt==null&&(be.endedAt=G,be.endReason==null&&(be.endReason=o==="recover"?"node-offline":"node-transfer")),le.push({agent:n,sessionId:X.sessionId??null,pinned:X.pinned===!0,startedAt:G,endedAt:null}),Te.epochs=le;let ae={id:e,name:t.trim(),cwd:X.cwd,workspace:_,leadBox:X.name,boxes:[Te],events:[],createdAt:Number.isFinite(f)?f:G,...J?{pendingHandoffHydrate:{artifact:jo,at:G}}:{}};return Ie(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:_.branch??null,wip:F,...I?{wipAdvanced:I}:{},epoch:le.length-1,name:ae.name,cwd:ae.cwd,workspace:{base:_.base??null,branch:_.branch??null,kind:_.kind,..._.baseRef?{baseRef:_.baseRef}:{},..._.repo?{repo:_.repo}:{},..._.repos?{repos:_.repos.map(({name:K,base:ue,baseRef:re,repo:V})=>({name:K,base:ue,baseRef:re,repo:V}))}:{}}}),k.tracks.push(ae),await ie(),{...await Ct(ae),adopt:{wipApplied:F,wipSkipped:A,...I?{wipAdvanced:I}:{},codeless:M,artifactSource:J}}}catch(L){for(let F of j)await $t(F.base,F.dir).catch(()=>{});throw await Q.rm(y,{recursive:!0,force:!0}).catch(()=>{}),L}}async function hE(e,t,n){let r=await ne(),o=ye(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ie())}async function oa(e,t){e.step=t,await dc(e)}function wE(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 gE(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function QF(e,t,n){if(!gE(n)||Mt.has(t)||!await xe(n.box).catch(()=>null))return!1;try{await Q.access(n.dir)}catch{return!1}let o=await xe(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),bE(s,o).catch(a=>yE(s,a)),!0}async function Lw(){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 QF(n,r,o)){t=!0,Ie(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ce({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=wE(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Gr.has(i.state)&&(i.state="canceled"),Ie(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 ce({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await zA(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Gr.has(r.state)||Mt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?te.join(ua,`${n.id}-${r.subtaskId}`):null;await ce({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await zA(n,r.name,o)}}return t=await eU(e)||t,t&&await ie(),t}async function zA(e,t,n){if(t&&await en(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 xe(e.leadBox).catch(()=>null);r?.cwd&&await $t(r.cwd,n).catch(()=>{})}async function eU(e){let t;try{t=await Q.readdir(ua)}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(ua,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 xe(a.leadBox).catch(()=>null):null;i?.cwd?await $t(i.cwd,s).catch(()=>{}):await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ce({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Uw(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Ms(n))throw new Error("task (string) required");let s=await sc(t,r),a=So(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 xe(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 Ns(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 Ns(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Uo().slice(0,8),f={id:d,trackId:e,trackName:i.name,agent:t,task:Ms(n),skipPermissions:r,reason:QA(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Fw(i.name)}-${d}`,base:null,repos:u,dir:te.join(ua,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Mt.set(d,f),await dc(f),tU(f,c,s).catch(p=>yE(f,p)),f}async function yE(e,t){e.failedStep=e.step,e.error=t instanceof rc?"canceled":t?.message||String(t),e.step=t instanceof rc?"canceled":"error",e.finishedAt=Date.now(),await Ww(e).catch(()=>{}),await dc(e).catch(()=>{}),await sU(e).catch(()=>{})}function pd(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 Hw(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 tU(e,t,n){if(await oa(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Ps(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await dc(e)}else e.base=await Ps(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await oa(e,"spawn"),await Q.mkdir(ua,{recursive:!0}),e.repos){await Q.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Ds(a.leadDir,te.join(e.dir,a.name),e.branch,a.base);let s=Pt((ye(await ne(),e.trackId)||{}).workspace);await lc(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 Ds(t.cwd,e.dir,e.branch,e.base),await ec(e.dir);await nU(e,t);let r=e.trackName.slice(0,38).trim(),o=await xr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await rU(e,o),await eE(e.trackId,o.name,eF(e.task,!!e.repos),{kind:"subtask"}),await bE(e,t)}async function bE(e,t){await oa(e,"working");let n=te.join(e.dir,Zu);if(await iU(n,e),e.canceled)throw new rc;if(await oa(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 nm(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:GA({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await sm(l.leadDir,l.base,e.branch,ra),l.files=await am(l.leadDir,l.base,e.branch,ra);let d=await im(l.leadDir,l.base,e.branch,ra);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(f=>te.join(l.name,f))),i.push(...l.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${l.name}: ${f}`:te.join(l.name,f))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await nm(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:GA({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await sm(t.cwd,e.base,e.branch,ra),e.files=await am(t.cwd,e.base,e.branch,ra);let s=await im(t.cwd,e.base,e.branch,ra);e.applied=s.applied,e.conflicts=s.conflicts}await dc(e);let r=await aU(e,t,n);await oa(e,"resume"),await eE(e.trackId,e.leadBox,tF(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await oa(e,"reap"),await Ww(e),e.step="done";let o=Date.now();await hE(e.trackId,e.id,{...fd(e),finishedAt:o}),await oU(e,o),e.finishedAt=o}async function nU(e,t){let n=te.join(e.dir,te.dirname(Ji));await Q.mkdir(n,{recursive:!0});let r="";try{r=await ok(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 yw(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 Q.writeFile(te.join(e.dir,
|
|
255
|
+
`;await Q.writeFile(te.join(e.dir,Ji),o)}async function rU(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:$a(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Ie(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]||{},...fd(e)},await ie())}async function oU(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),Ie(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 ie()}async function sU(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"),Ie(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]||{},...fd(e)},await ie()}async function aU(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 Ww(e){if(e.dir&&await Ef(e.dir).catch(()=>{}),e.box&&await en(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 xe(e.leadBox).catch(()=>null))?.cwd;n&&await $t(n,e.dir).catch(()=>{})}}async function iU(e,t){let n=(t.startedAt||Date.now())+JF;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 Fo(XF)}}function Fo(e){return new Promise(t=>setTimeout(t,e))}var YA,On,JA,XL,Yi,ZL,ed,oc,Kr,jo,XA,QL,Zu,Ji,eF,tF,Vt,Gh,ye,Vh,Ie,zh,Yh,oF,Gr,WA,iF,cF,lF,uF,pF,Xi,qA,wF,gF,yF,bF,kF,sa,Zi,TF,EF,_F,RF,cE,Qi,lE,NF,zi,Jh,PF,Xh,LF,tc,FF,UF,KA,Zh,ow,Cw,la,fa,sw,cn,aw,Nw,VF,Qh,JF,XF,ua,ra,Mt,ZF,qo,fd,dc,Fw,rc,Qn=U(()=>{ja();ut();$p();N0();si();wr();Tl();L0();lt();wn();rs();Sl();nx();po();Xp();Qp();bx();Rl();mp();Al();Sp();nn();zl();mr();xn();ai();Ql();Gx();mm();oT();jl();Dl();aT();ah();me();Au();Lu();$i();To();Hu();ea();Rh();zu();Bh();Vn();Ou();OA();Uh();BA();ul();LA();UA();Kh();YA=JL(YL),On=process.env.BOSTRAT_STATE_DIR||te.join(process.env.HOME||"/tmp",".bostrat"),JA=te.join(On,"tracks.json"),XL=te.join(On,"events.jsonl"),Yi=te.join(On,"workspaces"),ZL=process.env.BOSTRAT_DEFAULT_REPO||null,ed={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"}},oc=Object.keys(ed);Kr={at:0,value:null};jo=".bostrat/handoff/latest.md",XA=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;QL=/\(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\.\)$/;Zu=".bostrat/handoff/subtask-result.md",Ji=".bostrat/handoff/lead-context.md",eF=(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 ${Ji} \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 ${Zu} (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 ${Ji} \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 ${Zu} 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.`,tF=(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."),Vt=null,Gh=null;ye=(e,t)=>e.tracks.find(n=>n.id===t)||null,Vh=Promise.resolve();Ie=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),nF(e.id,r),Yx(e,r),r};ck(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&&(Ie(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await ie().catch(()=>{}))});zh=te.join(On,"plane-link","update-notice.json");Yh=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),oF=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Gr=new Set(["merged","conflict","canceled","failed","handed-off"]),WA=new Map;iF=5e3;cF={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};lF=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];uF=10*6e4;pF=1500,Xi=new Map;qA="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.";wF=4e3,gF=3,yF=[300,350,400],bF=1e3,kF=12,sa=new Map,Zi=new Map;TF=[250,400,500,600];EF=3e4,_F=[350,650,900];RF=12e4;cE=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}))});Qi="# bostrat workspace",lE=`${Qi}
|
|
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
|
|
@@ -550,6 +550,19 @@ travel off-box, and the server rejects token-shaped content. Anything worth
|
|
|
550
550
|
notifying about belongs in your chat message too; the notification is a nudge
|
|
551
551
|
toward the chat, never the only record.
|
|
552
552
|
|
|
553
|
+
When beginning a semantic git-recovery phase that Bostrat cannot observe at the
|
|
554
|
+
button boundary, declare it so the Track pill stays specific:
|
|
555
|
+
|
|
556
|
+
bostrat-status set fixing-ci --summary "Repairing the failing client test"
|
|
557
|
+
bostrat-status set reconcile --pr 641
|
|
558
|
+
bostrat-status set rebase
|
|
559
|
+
|
|
560
|
+
Clear it when the phase ends with \`bostrat-status clear\`. Declarations are
|
|
561
|
+
non-blocking, apply only to THIS Track, and never replace a real question or
|
|
562
|
+
approval \u2014 those still read needs you. Use \`bostrat-status\` with no arguments to
|
|
563
|
+
inspect the folded status. Do not declare ordinary work; Bostrat already derives
|
|
564
|
+
working from the live turn and settled when no obligation remains.
|
|
565
|
+
|
|
553
566
|
When you FINISH a piece of implementation work (feature built, bug fixed, checks
|
|
554
567
|
green) and the operator has not already told you to ship it: do NOT commit, push,
|
|
555
568
|
or open a PR on your own. Instead OFFER to, as a structured plan \u2014 write the ship
|
|
@@ -595,16 +608,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
595
608
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
596
609
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
597
610
|
follow-up on its own; the operator's tap is the gate.
|
|
598
|
-
`,
|
|
599
|
-
`)),
|
|
600
|
-
`,`${
|
|
611
|
+
`,NF=lE,zi=lE+zk;Jh=e=>typeof e=="string"&&(e===Qi||e.startsWith(`${Qi}
|
|
612
|
+
`)),PF="# bostrat multi-repo workspace";Xh=zi.replace(`${Qi}
|
|
613
|
+
`,`${Qi}
|
|
601
614
|
|
|
602
615
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
603
616
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
604
617
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
605
618
|
When working anywhere else, ignore everything below.
|
|
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=`
|
|
619
|
+
`);LF=new Set(["claude","codex","cursor"]);tc=new Set,FF=e=>tc.has(e);UF=9e4;KA={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Zh=new Map;ow=120;Cw=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:$a(e.hookToken),createdAt:Date.now()});la=new Map,fa=e=>la.get(e)||(Vt?ye(Vt,e)?.handoff:null)||null;sw=6e4,cn=(e,t,n=15e3)=>YA("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),aw=new Map,Nw=e=>aw.get(e)||null;VF=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;Qh=new Map;JF=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,XF=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ua=te.join(On,"worktrees"),ra=[Ji,Zu],Mt=new Map,ZF=e=>Mt.get(e)||(Vt?Vt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,qo=e=>{let t=new Map([...Mt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Vt?ye(Vt,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)},fd=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});dc=e=>hE(e.trackId,e.id,fd(e));Fw=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",rc=class extends Error{}});ut();import Ae from"node:path";import ht from"node:fs";import _q from"node:readline";import{fileURLToPath as Iq}from"node:url";import jt from"express";import $q from"node:http";import Rq from"node:https";import ef from"node:os";import{randomUUID as Oq}from"node:crypto";import{WebSocketServer as Cq}from"ws";ut();import SC from"node-pty";async function Db(e,t,n={}){if(!Le(t)){xl(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await kr()){xl(e,`host tmux server not running on ${It}`),e.close(1011,"tmux server unavailable");return}if(!await xe(t)){xl(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Pb(n.cols,120,Bb),o=Pb(n.rows,32,jb),s;try{s=SC.spawn(Y,["-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){xl(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Uf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Uf(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"&&_C(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{}EC(t)})}var TC=6,AC=250;async function EC(e,t=TC,n=AC){for(let r=0;r<t;r++){if(await lo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Bb=20,jb=6;function _C(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Bb&&Number(t)>=jb}function Pb(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Uf(e,t){try{e.send(JSON.stringify(t))}catch{}}function xl(e,t){Uf(e,{type:"error",message:t})}function Lb(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}}nn();Sl();$p();Qn();import{createRequire as Mq}from"node:module";var cU=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function qw({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(cU)&&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 CE from"node:fs";import zr from"node:fs/promises";import AU from"node:os";import ct from"node:path";import{spawn as EU}from"node:child_process";import{createRequire as _U}from"node:module";me();Ql();import $E from"node:fs";import pc from"node:fs/promises";import dU from"node:http";import un from"node:path";import hd from"node:os";import{createRequire as fU}from"node:module";import Gw from"ws";import lU from"node:path";import uU from"node:os";var fc=process.env.BOSTRAT_STATE_DIR||lU.join(process.env.HOME||uU.tmpdir(),".bostrat");function Kw(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 xE(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Kw(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function vE(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 ln=(()=>{try{return fU(import.meta.url)("../package.json").version||null}catch{return null}})(),SE=process.env.BOSTRAT_STATE_DIR||un.join(process.env.HOME||hd.tmpdir(),".bostrat");function Ko({env:e=process.env,pkgDir:t=un.resolve(un.dirname(new URL(import.meta.url).pathname),".."),exists:n=$E.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(un.join(t,"..",".git"))?"git-checkout":"dev"}function mc(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 md(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 pU({home:e=process.env.HOME||hd.homedir(),env:t=process.env}={}){let n=r=>{try{return $E.statSync(r),!0}catch{return!1}};return{claude:n(un.join(e,".claude",".credentials.json")),codex:n(un.join(t.CODEX_HOME||un.join(e,".codex"),"auth.json"))}}var it=1,TE=1e3,AE=3e4,mU=6e4,hU=1500,wU=500,Vr=100,gU=2e3,yU=25e3,bU=6e4,RE=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},kU=RE(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),xU=3e4,EE=10*6e4,vU=400,_E=250,SU=RE(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Vw=4,TU=6e4,IE=6e4;function OE({url:e,token:t,nodeId:n=hd.hostname(),name:r=hd.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=TU,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:d=null,updatePolicy:f=null,installKind:p=Ko(),selfUrl:h=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:w=null,onDegraded:g=null,onRecovered:k=null,onConnected:x=null,ingestMode:y=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:_=kU,unackedRetryMs:M=bU,pingMs:j=yU,maxInflightEvents:L=vU,httpTimeoutMs:F=SU,stallBackoffMinMs:A=xU,fetchImpl:I=globalThis.fetch,log:C=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=vE(e);let J=h.replace(/^http/,"ws"),q=new Set,R=new Map,W=new Map,X=un.join(SE,"plane-link","cursor.json"),G=un.join(SE,"plane-link","dead-letter.ndjson"),Te=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),le={},be={},ae=new Map,K=null,ue=null,re=0,V=0,$e=0,Me=Promise.resolve(),fe="ws",E=0,P=!1,he=0,Ze=!1,Be=Vr,Et=new Set,b=[],m=null,v=null,O=!1,de=!1,ge=TE,He=null,Lt=null,Ne=null,Xt=null,Pn=null,rf=!1,qc=0;async function eR(){try{le=JSON.parse(await pc.readFile(X,"utf8"))}catch{le={}}}function tR(){rf=!0,!Ne&&(Ne=setTimeout(async()=>{if(Ne=null,!!rf){rf=!1;try{await pc.mkdir(un.dirname(X),{recursive:!0}),await pc.writeFile(X,JSON.stringify(le))}catch(D){C.error("[plane-link] cursor flush failed:",D?.message||D)}}},gU),Ne.unref?.())}function at(D){return!v||v.readyState!==Gw.OPEN?!1:(v.send(JSON.stringify(D)),!0)}function Ea(D){let B=Date.now();for(let{trackId:T,event:N}of D){let z=ae.get(N.id);ae.set(N.id,{trackId:T,seq:N.seq,event:N,sentAt:B,firstSentAt:z?.firstSentAt??B,retryAt:B+M,tries:(z?.tries??0)+1,nacks:z?.nacks??0})}return fe==="http"?(sR(D),!0):at({v:it,ch:"events",type:"append",events:D})}function nR(D){let B=null;for(let T of ae.values())T.trackId===D&&(B==null||T.seq<B)&&(B=T.seq);return B}function of(D){let B=be[D];if(B==null)return;let T=nR(D),N=T==null?B:Math.min(B,T-1);N>(le[D]??0)&&(le[D]=N,tR())}function _a(){if(K=Date.now(),ue!=null){let D=Date.now()-ue;C.log(`[plane-link] spine sync recovered after ${Math.round(D/1e3)}s degraded (${re} stall redial(s))`);let B=ue;ue=null,re=0,V=0,P=!1,he=0;try{k?.({since:B,downMs:D})}catch(T){C.error(`[plane-link] onRecovered failed: ${T?.message||T}`)}}}function sf(D){if(!Array.isArray(D)||!D.length)return;let B=new Set;for(let T of D){let N=ae.get(T);N&&(ae.delete(T),(be[N.trackId]??0)<N.seq&&(be[N.trackId]=N.seq),B.add(N.trackId))}for(let T of B)of(T);_a()}function uy(D,B){$e++;let T={at:Date.now(),trackId:D.trackId,seq:D.seq,id:D.event?.id??null,type:D.event?.type??null,reason:B,event:D.event??null};C.error(`[plane-link] DEAD-LETTER ${D.trackId} seq ${D.seq} (${T.type??"?"}): plane rejected it permanently (${B}) \u2014 kept in ${G}`),Me=Me.then(async()=>{await pc.mkdir(un.dirname(G),{recursive:!0}),await pc.appendFile(G,JSON.stringify(T)+`
|
|
620
|
+
`)}).catch(N=>C.error(`[plane-link] dead-letter write failed: ${N?.message||N}`))}function dy(D,B,T,N=null){if(!Array.isArray(D)||!D.length)return;let z=new Set,ve=Date.now();for(let Ee of D){let Fe=ae.get(Ee);Fe&&(T?(ae.delete(Ee),uy(Fe,B),(be[Fe.trackId]??0)<Fe.seq&&(be[Fe.trackId]=Fe.seq),z.add(Fe.trackId)):(Fe.nacks++,Fe.retryAt=ve+Math.max(1e3,Number(N)||M)))}for(let Ee of z)of(Ee);_a()}function fy(D=Date.now()){let B=1/0;for(let T of ae.values())T.firstSentAt<B&&(B=T.firstSentAt);return B===1/0?0:D-B}function py(){if(!O||!ae.size)return;let D=Date.now(),B=fy(D);if(B<_||K!=null&&D-K<_||fe==="http"&&P||V>D)return;re++;let T=Math.min(A*2**(re-1),EE);if(ue==null){ue=D-B,C.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(B/1e3)}s with ${ae.size} event(s) in flight (${fe} door) \u2014 the plane is not answering appends`);try{g?.({since:ue,unacked:ae.size,oldestAgeMs:B,mode:fe})}catch(N){C.error(`[plane-link] onDegraded failed: ${N?.message||N}`)}}if(fe==="http"){P=!0,he=D+T,C.error(`[plane-link] ingest circuit OPEN (stall #${re}); probing /v1/ingest/health in ${Math.round(T/1e3)}s`);return}V=D+T,C.error(`[plane-link] closing the link and redialing in ${Math.round(T/1e3)}s (stall redial #${re})`);try{v?.terminate()}catch{}}function af(){if(!O)return;let D=Date.now();if(fe==="http"&&P)return;let B=[...ae.values()].filter(N=>N.event&&N.retryAt<=D&&!(fe==="http"&&Et.has(N.trackId)));if(!B.length)return;B.sort((N,z)=>N.trackId===z.trackId?N.seq-z.seq:N.trackId<z.trackId?-1:1),C.log(`[plane-link] re-sending ${B.length} unacked event(s) the plane never confirmed`);let T=fe==="http"?Be:Vr;if(fe==="http"){let N=new Map;for(let z of B)(N.get(z.trackId)??N.set(z.trackId,[]).get(z.trackId)).push(z);for(let[,z]of N){if(E>=Vw)return;Ea(z.slice(0,T).map(ve=>({trackId:ve.trackId,event:ve.event})))}return}for(let N=0;N<B.length;N+=T){let z=B.slice(N,N+T).map(ve=>({trackId:ve.trackId,event:ve.event}));if(!Ea(z))return}}async function rR(D=null){for(;;){if(!O)return!1;let B=ae.size<L,T=fe!=="http"||E<Vw&&!P&&(!D||!Et.has(D));if(B&&T)return!0;await new Promise(N=>setTimeout(N,100))}}function cf(){if(m=null,!O||!b.length)return;let D=b;if(b=[],fe!=="http"){for(let T=0;T<D.length;T+=Vr)Ea(D.slice(T,T+Vr));return}let B=new Map;for(let T of D)(B.get(T.trackId)??B.set(T.trackId,[]).get(T.trackId)).push(T);for(let[T,N]of B){if(Et.has(T)||E>=Vw||P){b.push(...N);continue}for(let z=0;z<N.length;z+=Be)Ea(N.slice(z,z+Be))}b.length&&!m&&(m=setTimeout(cf,_E),m.unref?.())}let oR=D=>{let B=new Map;for(let{trackId:T,event:N}of D)(B.get(T)??B.set(T,[]).get(T)).push(N);return[...B].map(([T,N])=>({trackId:T,events:N}))},ns=(D,B,T,{progress:N})=>{let z=Date.now();for(let{event:ve}of D){let Ee=ae.get(ve.id);Ee&&(Ee.nacks++,Ee.retryAt=z+Math.max(1e3,T||M))}N&&_a()};async function sR(D){let B=new Set(D.map(T=>T.trackId));for(let T of B)Et.add(T);E++;try{let T=await I(`${Te}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:oR(D)}),signal:AbortSignal.timeout(F)}),N=(Number(T.headers.get("retry-after"))||0)*1e3;if(T.status===200){let z=await T.json();Be=Vr;for(let ve of z.results??[]){sf(ve.acked??[]);let Ee=(Fe,xt)=>{let Ft=new Map;for(let Qe of Fe??[])(Ft.get(Qe.reason)??Ft.set(Qe.reason,[]).get(Qe.reason)).push(Qe.id);for(let[Qe,yy]of Ft)dy(yy,Qe,xt,z.retryAfterMs??null)};Ee(ve.rejected,!0),Ee(ve.deferred,!1)}sf(z.discarded??[]),_a();return}if(T.status===429){ns(D,"overloaded",N||5e3,{progress:!0});return}if(T.status===413&&D.length>1){Be=Math.max(1,Math.floor(D.length/2)),C.error(`[plane-link] ingest request too large (${D.length} events) \u2014 halving batches to ${Be}`),ns(D,"too_large",1e3,{progress:!0});return}if(T.status===413){let z=ae.get(D[0].event.id);z&&(ae.delete(z.event.id),uy(z,"too_large"),(be[z.trackId]??0)<z.seq&&(be[z.trackId]=z.seq),of(z.trackId)),_a();return}if(T.status===401||T.status===403){if(Date.now()-qc>=IE){qc=Date.now();let z="";try{z=(await T.json())?.error??""}catch{}C.error(`[plane-link] plane refused ingest (HTTP ${T.status}${z?`: ${z}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}ns(D,`http_${T.status}`,6e4,{progress:!1});return}if(T.status===400){C.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${D.length} event(s) for retry; this is a bug, not data`),ns(D,"http_400",6e4,{progress:!1});return}ns(D,`http_${T.status}`,N||5e3,{progress:!1})}catch(T){let N=T?.name==="TimeoutError"||T?.name==="AbortError";N&&D.length>1&&(Be=Math.max(1,Math.floor(D.length/2)),C.error(`[plane-link] ingest request of ${D.length} event(s) outlived its ${F} ms deadline \u2014 halving batches to ${Be}`)),ns(D,N?"timeout":"network",5e3,{progress:!1})}finally{E--;for(let T of B)Et.delete(T)}}async function aR(){if(fe!=="http"||!P||!O||Ze)return;let D=Date.now();if(!(he>D)){Ze=!0;try{let B=await I(`${Te}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(F)});if(!B.ok)throw new Error(`HTTP ${B.status}`);C.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),P=!1,he=0,V=Date.now()+F+M+1e3;for(let T of ae.values())T.retryAt=Math.min(T.retryAt,Date.now());af()}catch(B){re++;let T=Math.min(A*2**(re-1),EE);he=Date.now()+T,C.error(`[plane-link] ingest door still not answering (${B?.message||B}); next probe in ${Math.round(T/1e3)}s`)}finally{Ze=!1}}}function my(){let D=Date.now();return{mode:fe,unacked:ae.size,oldestUnackedAgeMs:fy(D),lastProgressAt:K,degradedSince:ue,stallRedials:re,deadLettered:$e,http:fe==="http"?{inFlight:E,circuitOpen:P,nextProbeInMs:P?Math.max(0,he-D):0,batchCap:Be}:null}}async function hy(){if(!i)return;let D;try{D=await i()}catch(B){C.error("[plane-link] metrics sample failed:",B?.message||B);return}D&&at({v:it,ch:"ctl",type:"metrics",metrics:{...D,planeLink:my()}})}function iR(){i&&(Pn&&clearInterval(Pn),hy(),Pn=setInterval(hy,c),Pn.unref?.())}async function cR(){let D;try{D=await o()}catch(B){C.error("[plane-link] backfill listTracks failed:",B?.message||B);return}at({v:it,ch:"ctl",type:"tracks",ids:D.filter(B=>!B.transferredTo).map(B=>B.id).filter(B=>typeof B=="string")});for(let B of D){if(!O)return;let T;try{T=await H(B)}catch{continue}let N=le[B.id]??0,z=T.filter(Ee=>Ee.seq>N&&typeof Ee.id=="string"),ve=fe==="http"?Be:Vr;for(let Ee=0;Ee<z.length;Ee+=ve){if(!await rR(B.id))return;let Fe=z.slice(Ee,Ee+Math.min(ve,fe==="http"?Be:Vr)).map(xt=>({trackId:B.id,event:xt}));if(!Ea(Fe))return}}}function wy(D){!O||!D?.trackId||typeof D.id!="string"||(b.push({trackId:D.trackId,event:D}),m||(m=setTimeout(cf,_E),m.unref?.()))}let Ia=new Map;async function lR(D){let{reqId:B,method:T,path:N,headers:z={},body:ve}=D,Ee=Object.fromEntries(Object.entries(z).filter(([,xt])=>xt!=null)),Fe=new AbortController;Ia.set(B,Fe);try{if(D.host){await uR(D,Ee,Fe);return}w&&(Ee["x-bostrat-token"]=w);let xt=await fetch(h+N,{method:T,headers:Ee,body:ve?Buffer.from(ve,"base64"):void 0,signal:Fe.signal}),Ft=Object.fromEntries(xt.headers);for(let Qe of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Ft[Qe];if(Ft["content-type"]||(Ft["content-type"]="application/octet-stream"),at({v:it,ch:"control",type:"http_head",reqId:B,status:xt.status,headers:Ft}),xt.body){for await(let Qe of xt.body)if(!at({v:it,ch:"control",type:"http_data",reqId:B,data:Buffer.from(Qe).toString("base64")}))break}at({v:it,ch:"control",type:"http_end",reqId:B,error:null})}finally{Ia.delete(B)}}function uR(D,B,T){let{reqId:N,method:z,path:ve,body:Ee}=D,Fe=new URL(h);return new Promise(xt=>{let Ft=dU.request({host:Fe.hostname,port:Fe.port,method:z,path:ve,headers:{...B,host:D.host},signal:T.signal},Qe=>{let{connection:yy,"keep-alive":Xq,"transfer-encoding":Zq,upgrade:Qq,...dR}=Qe.headers;at({v:it,ch:"control",type:"http_head",reqId:N,status:Qe.statusCode||502,headers:dR}),Qe.on("data",fR=>{at({v:it,ch:"control",type:"http_data",reqId:N,data:Buffer.from(fR).toString("base64")})||Qe.destroy()}),Qe.on("end",()=>{at({v:it,ch:"control",type:"http_end",reqId:N,error:null}),xt()}),Qe.on("error",()=>{at({v:it,ch:"control",type:"http_end",reqId:N,error:"view upstream error"}),xt()})});Ft.on("error",Qe=>{at({v:it,ch:"control",type:"http_end",reqId:N,error:Qe?.message||"view request failed"}),xt()}),Ee&&Ft.write(Buffer.from(Ee,"base64")),Ft.end()})}function gy(){if(de)return;v=new Gw(e,{headers:{authorization:`Bearer ${t}`}}),v.on("open",async()=>{let B=null;try{B=await s?.()||null}catch{B=null}at({v:it,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:ln,installKind:p,...typeof f=="function"?{updatePolicy:f()}:f?{updatePolicy:f}:{},providers:pU(),...B?{agents:B}:{}})}),v.on("message",B=>{let T;try{T=JSON.parse(B)}catch{return}if(T.ch==="ctl"&&T.type==="hello_ok"){O=!0,ge=TE,He=null,C.log(`[plane-link] connected to plane as ${n}`);try{x?.()}catch{}T.minNodeVersion&&ln&&mc(ln,T.minNodeVersion)&&C.error(`[plane-link] this node is bostrat-node ${ln}; the plane asks for \u2265${T.minNodeVersion} \u2014 update with: ${md(p)}`);try{u?.(typeof T.updatePolicy=="string"?T.updatePolicy:null)}catch{}let N=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;if(N&&ln&&mc(ln,N)&&!q.has(N)){q.add(N),C.log(`[plane-link] bostrat-node ${N} is available (this node runs ${ln}) \u2014 ${md(p)}`);try{l?.({current:ln,latest:N,min:T.minNodeVersion??null,installKind:p,required:!!(T.minNodeVersion&&mc(ln,T.minNodeVersion))})}catch(ve){C.error(`[plane-link] update notice failed: ${ve?.message||ve}`)}}let z=T.ingest==="http";fe=y==="ws"?"ws":y==="http"||z?"http":"ws",y==="http"&&!z&&C.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),fe==="http"&&C.log("[plane-link] spine rides the HTTPS ingest door"),P=!1,he=0,Be=Vr,Xt&&clearInterval(Xt),Xt=setInterval(()=>{at({v:it,ch:"ctl",type:"ping"}),py(),aR().catch(()=>{}),af(),b.length&&!m&&cf()},j),Xt.unref?.(),iR(),cR().catch(ve=>C.error("[plane-link] backfill failed:",ve?.message||ve));return}if(T.ch==="ctl"&&T.type==="hello_refused"){let N=typeof T.minNodeVersion=="string"?T.minNodeVersion:null,z=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;C.error(`[plane-link] plane REFUSED this node: bostrat-node ${ln??"?"} is below its minimum ${N??"?"} \u2014 ${T.fix||md(p)}`),ge=AE,He=0;try{d?.({current:ln,min:N,latest:z,fix:T.fix||md(p),installKind:p})}catch(ve){C.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"){Zl(T.caps??{},{source:"plane",updatedAt:T.updatedAt??Date.now()}).then(N=>C.log(`[plane-link] spend caps ${Object.keys(N.caps).length?`updated (${Object.keys(N.caps).join(", ")})`:"cleared"} from plane`)).catch(N=>C.error("[plane-link] spend-caps push rejected:",N?.message||N));return}if(T.ch==="ctl"&&T.type==="transferred"&&Array.isArray(T.tracks)){Promise.resolve().then(()=>a?.(T.tracks)).catch(N=>C.error("[plane-link] transfer notice failed:",N?.message||N));return}if(T.ch==="events"&&T.type==="ack"&&Array.isArray(T.ids)){sf(T.ids);return}if(T.ch==="events"&&T.type==="nack"&&Array.isArray(T.ids)){dy(T.ids,T.reason,T.permanent===!0,T.retryAfterMs??null);return}if(T.ch==="control"&&T.type==="http_open"){lR(T).catch(N=>at({v:it,ch:"control",type:"http_end",reqId:T.reqId,error:N?.message||"tunnel error"}));return}if(T.ch==="control"&&T.type==="http_cancel"){Ia.get(T.reqId)?.abort();return}if(T.ch==="attach"&&T.type==="open"){let N=new Gw(J+T.path,Array.isArray(T.protocols)?T.protocols:[],T.host?{headers:{host:T.host}}:void 0);R.set(T.streamId,N),W.set(T.streamId,[]),N.on("open",()=>{let ve=W.get(T.streamId)||[];W.delete(T.streamId);for(let{buf:Ee,bin:Fe}of ve)N.send(Ee,{binary:Fe})}),N.on("message",(ve,Ee)=>{let Fe=Ee?ve:Buffer.from(String(ve));at({v:it,ch:"attach",type:"data",streamId:T.streamId,data:Fe.toString("base64"),...Ee?{bin:!0}:{}})});let z=()=>{W.delete(T.streamId),R.delete(T.streamId)&&at({v:it,ch:"attach",type:"close",streamId:T.streamId})};N.on("close",z),N.on("error",z);return}if(T.ch==="attach"&&T.type==="data"){let N=R.get(T.streamId);if(!N)return;let z=Buffer.from(T.data,"base64"),ve=T.bin===!0,Ee=W.get(T.streamId);Ee?Ee.push({buf:z,bin:ve}):N.send(z,{binary:ve});return}if(T.ch==="attach"&&T.type==="close"){let N=R.get(T.streamId);R.delete(T.streamId),W.delete(T.streamId),N?.close();return}});let D=()=>{O&&C.log("[plane-link] disconnected from plane"),O=!1,Xt&&(clearInterval(Xt),Xt=null),Pn&&(clearInterval(Pn),Pn=null),ae.clear(),be={},b=[],m&&(clearTimeout(m),m=null),Et.clear();for(let N of R.values())N.close();R.clear(),W.clear();for(let N of Ia.values())N.abort();if(Ia.clear(),de||Lt)return;He==null&&(He=Date.now());let B=Date.now()-He<mU,T=Math.max(B?hU+Math.random()*wU:ge,V-Date.now());Lt=setTimeout(()=>{Lt=null,gy()},T),Lt.unref?.(),B||(ge=Math.min(ge*2,AE))};v.on("close",D),v.on("error",B=>{let T=/Unexpected server response: (\d+)/.exec(B?.message||"")?.[1];(T==="401"||T==="403")&&Date.now()-qc>=IE&&(qc=Date.now(),C.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)`)),D()})}return rt.on("event",wy),eR().then(gy),{stop:()=>{de=!0,rt.off("event",wy),Lt&&clearTimeout(Lt),Ne&&clearTimeout(Ne),Xt&&clearInterval(Xt),Pn&&clearInterval(Pn),m&&clearTimeout(m),at({v:it,ch:"ctl",type:"bye",reason:"shutdown"}),v?.close()},status:()=>({connected:O,nodeId:n,cursor:{...le},inflight:ae.size,...my()}),_tick:()=>{py(),af()}}}Tl();var wd=(()=>{try{return _U(import.meta.url)("../package.json").version||null}catch{return null}})(),IU=process.env.BOSTRAT_STATE_DIR||ct.join(process.env.HOME||AU.tmpdir(),".bostrat"),ME=ct.join(IU,"self-update"),gd=ct.join(ME,"status.json"),$U=1440*6e4,RU=6e4,hc=new Set(["npm-global","git-checkout"]);function NE({kind:e=Ko(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:hc.has(e)?e==="git-checkout"||OU(t)?"auto":"prompt":"off"}function OU(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Dt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function CU({kind:e,to:t,from:n=wd,statusFile:r=gd,logFile:o=ct.join(ME,"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||ct.resolve(ct.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!hc.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(g,k="")=>`write_status ${Dt(g)} ${k}`,f=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Dt(i)}`,p=`
|
|
608
621
|
health() {
|
|
609
622
|
for i in $(seq 1 45); do
|
|
610
623
|
sleep 1
|
|
@@ -627,16 +640,16 @@ write_status() { # <status> [error]
|
|
|
627
640
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
628
641
|
}
|
|
629
642
|
${p}
|
|
630
|
-
`;if(e==="npm-global"){let g=a?
|
|
631
|
-
`:"",
|
|
632
|
-
${
|
|
633
|
-
if ! ${
|
|
634
|
-
${
|
|
643
|
+
`;if(e==="npm-global"){let g=a?ct.join(a,"bin","npm"):"npm",k=a?`export PATH=${Dt(ct.join(a,"bin"))}:"$PATH"
|
|
644
|
+
`:"",x=a?` --prefix ${Dt(a)}`:"",y=_=>`${Dt(g)} install -g "bostrat-node@${_}" --no-audit --no-fund${x}`;return h+k+`
|
|
645
|
+
${d("updating",'"" ')}
|
|
646
|
+
if ! ${y("$TO")}; then
|
|
647
|
+
${d("failed",'"npm install failed"')}
|
|
635
648
|
exit 1
|
|
636
649
|
fi
|
|
637
|
-
${
|
|
638
|
-
if ! ${
|
|
639
|
-
${
|
|
650
|
+
${d("restarting")}
|
|
651
|
+
if ! ${f}; then
|
|
652
|
+
${d("failed",'"service restart failed"')}
|
|
640
653
|
exit 1
|
|
641
654
|
fi
|
|
642
655
|
if health; then
|
|
@@ -644,12 +657,12 @@ if health; then
|
|
|
644
657
|
exit 0
|
|
645
658
|
fi
|
|
646
659
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
647
|
-
${
|
|
648
|
-
if [ -n "$FROM" ] && ${
|
|
649
|
-
${
|
|
660
|
+
${d("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
661
|
+
if [ -n "$FROM" ] && ${y("$FROM")} && ${f} && health; then
|
|
662
|
+
${d("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
650
663
|
exit 1
|
|
651
664
|
fi
|
|
652
|
-
${
|
|
665
|
+
${d("failed",'"agent did not answer after the update AND the rollback"')}
|
|
653
666
|
exit 1
|
|
654
667
|
`}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
668
|
LIVE=${Dt(l)}
|
|
@@ -658,76 +671,76 @@ trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
|
658
671
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
659
672
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
660
673
|
# and nothing here may write after that boot or the settle would repeat.
|
|
661
|
-
${
|
|
674
|
+
${d("restarting")}
|
|
662
675
|
if ${w("$TO")}; then
|
|
663
676
|
exit 0
|
|
664
677
|
fi
|
|
665
|
-
${
|
|
678
|
+
${d("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
666
679
|
if [ -n "$FROM" ] && ${w("$FROM")}; then
|
|
667
|
-
${
|
|
680
|
+
${d("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
668
681
|
exit 1
|
|
669
682
|
fi
|
|
670
|
-
${
|
|
683
|
+
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
671
684
|
exit 1
|
|
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(...
|
|
685
|
+
`}async function wc(e=gd){try{return JSON.parse(await zr.readFile(e,"utf8"))}catch{return null}}var MU=new Set(["updating","restarting","rolling-back"]),yd=e=>!!(e&&MU.has(e.status));async function bd({to:e,from:t=wd,kind:n=Ko(),reason:r="manual",statusFile:o=gd,spawnImpl:s=EU,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!hc.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!mc(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await wc(o);if(yd(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 zr.mkdir(ct.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?op(await NU())||ct.resolve(ct.dirname(process.execPath),".."):null,d=CU({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),f=ct.join(ct.dirname(o),`update-${e}.sh`),p=ct.join(ct.dirname(o),"last.log");await zr.writeFile(f,d,{mode:448}),await zr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let h=CE.existsSync("/run/systemd/system")&&await PU("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",f]]:["setsid",["-f","bash","-c",`exec bash ${Dt(f)} >> ${Dt(p)} 2>&1`]];try{s(h[0],h[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await zr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${w?.message||w}`})),{ok:!1,reason:`could not spawn updater: ${w?.message||w}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:f,statusFile:o}}async function NU(){try{return await zr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function PU(e){for(let t of String(process.env.PATH||"").split(ct.delimiter))try{return await zr.access(ct.join(t,e),CE.constants.X_OK),!0}catch{}return!1}var DU=45*6e4;async function PE({statusFile:e=gd,current:t=wd,now:n=Date.now,staleMs:r=DU}={}){let o=await wc(e);if(!o)return null;let s=a=>zr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(yd(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 DE({isBusy:e,start:t=bd,onStuck:n=null,onStarted:r=null,tickMs:o=RU,stuckMs:s=$U,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},f=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let h=u;d();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=wd,kind:w=Ko()}){return u?.to===p?!1:(d(),u={to:p,from:h,kind:w,since:a(),stuckFired:!1,timer:i(f,o)},u.timer.unref?.(),!0)},tick:f,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}me();lt();var BU=(...e)=>fetch(...e);function jU(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function zw(e=process.env){if(!jU(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(`${Re(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 pa(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await zw(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 kd(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();lt();var LU=(...e)=>fetch(...e);async function xd(e,t,n,r=process.env){if(!(Re(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 LU(`${Re(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 FU(e,t=process.env){return xd("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function BE({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await FU(t,o):await pa(e,o),a=await xd("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Yw(e,t=process.env){let n=await pa(e,t),r=await xd("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Jw(e,t=process.env){return(await xd("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function jE(e,t,n,r=process.env){let{blueprint:o,items:s}=await Yw(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();lt();var UU=(...e)=>fetch(...e);async function HU(e,t,n,r=process.env){if(!(Re(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 UU(`${Re(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 LE=72,Xw=160;async function FE(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>LE)throw new Error(`the title is the choice as a LABEL \u2014 ${LE} 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 ${Xw} chars) a reader can act on without opening the record`);if(c.length>Xw)throw new Error(`the summary is one sentence \u2014 ${Xw} 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 UE(e,t=process.env){return HU("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Bh();me();import vc from"node:fs/promises";import Rd from"node:path";import C4 from"node:os";wr();import WU from"node:fs/promises";import HE from"node:path";import qU from"node:os";var WE=()=>HE.join(process.env.BOSTRAT_STATE_DIR||HE.join(process.env.HOME||qU.tmpdir(),".bostrat"),"integrations.json");async function ur(){try{return JSON.parse(await WU.readFile(WE(),"utf8"))||{}}catch{return{}}}var vd=["linked","prOpened","prMerged","create"],Zw=["prompt","auto","off"],KU={create:["prompt","off"]},qE=e=>KU[e]??Zw;function Sd(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of vd){let s=qE(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 Go(){let e=await ur(),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:Sd(e),defaultTeam:n}:null}async function Td(){let e=await ur(),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:Sd(e)}}async function Qw(){return Sd(await ur())}async function KE(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${vd.join(", ")}`);for(let[r,o]of t){if(!vd.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${vd.join(", ")}`);let s=qE(r);if(!s.includes(o))throw new Error(s.length<Zw.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Zw.join(", ")}`)}let n=await ur();return n.ticketFlow={...Sd(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await St(WE(),n,{mode:384}),n.ticketFlow}async function gc(){let e=(await ur()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function GU(){let e=(await ur()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var VU={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Ad(){let t=(await ur()).voiceChat||{},n=t.provider||"openai",r=VU[n];if(!r)return null;let o=n==="openai"?await GU():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function GE(){let e=(await ur()).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 Vo(){let e=(await ur()).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();yr();$r();ea();import bc from"node:fs/promises";import t_ from"node:path";import y4 from"node:os";import{randomUUID as n_}from"node:crypto";var tg={};lf(tg,{createComment:()=>n4,findIssueUrl:()=>XU,issueStates:()=>s4,mdToAdf:()=>YE,moveIssue:()=>a4,parseIssueRef:()=>JU,resolveIssue:()=>ZU,uploadImage:()=>r4,upsertCard:()=>QU,whoami:()=>eg});var ma=e=>String(e.baseUrl||"").replace(/\/$/,""),zE=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function zo(e,t,n,r){let o=await fetch(`${ma(e)}${n}`,{method:t,headers:{authorization:zE(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 zU=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,YU=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function JU(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(zU);return r?r[1].toUpperCase():null}let n=t.match(YU);return n?n[1]:null}function XU(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 ZU(e,t){let n;try{n=await zo(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:`${ma(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function QU(e,t,{url:n,title:r,subtitle:o}){await zo(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 e4=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function t4(e){let t=[],n=0;for(let r of e.matchAll(e4)){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 YE(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
686
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...t4(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 n4(e,t,n){let r=await zo(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:YE(n)});return r?.id?`${ma(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function eg(e){let t=await zo(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(ma(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function r4(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(`${ma(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:zE(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??`${ma(e)}/rest/api/3/attachment/content/${c.id}`}var o4={new:"unstarted",indeterminate:"started",done:"completed"},VE=e=>o4[e?.key]??"started";async function s4(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([zo(e,"GET",`/rest/api/3/issue/${n}?fields=status`),zo(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:VE(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:VE(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function a4(e,t,n){await zo(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var sg={};lf(sg,{createComment:()=>m4,createIssue:()=>og,createProject:()=>rg,findIssueUrl:()=>d4,issueStates:()=>w4,moveIssue:()=>g4,parseIssueRef:()=>u4,resolveIssue:()=>f4,teams:()=>ha,uploadImage:()=>h4,upsertCard:()=>p4,whoami:()=>ng});var i4=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Mn(e,t,n={}){let r=await fetch(i4(),{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 c4=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,l4=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function u4(e){let t=String(e||"").trim(),n=t.match(c4);if(n)return n[1].toUpperCase();let r=t.match(l4);return r?r[1]:null}function d4(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 f4(e,t){let r=(await Mn(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 p4(e,t,{url:n,title:r,subtitle:o}){let s=await Mn(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 m4(e,t,n){let r=await Mn(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 ng(e){let t=await Mn(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 h4(e,{filename:t,contentType:n,buffer:r}){let o=await Mn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
674
687
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
675
688
|
success
|
|
676
689
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
677
690
|
}
|
|
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?`
|
|
691
|
+
}`,{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 w4(e,t){let n=await Mn(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 g4(e,t,n){if(!(await Mn(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 ha(e){return((await Mn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var JE=255;async function rg(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>JE,a=s?`${o.slice(0,JE-1).trimEnd()}\u2026`:o,i=await Mn(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 og(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Mn(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 r_={linear:{api:sg,config:Go},jira:{api:tg,config:Td}};async function fg(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(r_)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Yo(e){let t=r_[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function pg(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 o_=()=>process.env.BOSTRAT_STATE_DIR||t_.join(process.env.HOME||y4.tmpdir(),".bostrat"),lg=()=>t_.join(o_(),"tickets.json"),s_=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Ed=e=>`${s_()}/track/${e}`,Yr=null,ag=null,ig=Promise.resolve();async function xc(){return Yr||(ag??=bc.readFile(lg(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Yr=e,!t&&je()){let{records:n,source:r}=await ot("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;Yr[s]=i}Object.keys(Yr).length&&await ug().catch(()=>{})}}return Yr}).finally(()=>ag=null),ag)}async function ug(){let e=JSON.stringify({tickets:Yr},null,2),t=`${lg()}.${process.pid}.tmp`;await bc.mkdir(o_(),{recursive:!0}),await bc.writeFile(t,e),await bc.rename(t,lg())}async function Jr(e=null){if(ig=ig.then(ug,ug),await ig.catch(()=>{}),!e)return;let t=Yr?.[e];t?wt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Zt("tickets",e).catch(()=>{})}async function Nn(e){return(await xc())[e]??null}async function a_(){return{...await xc()}}async function i_(){return(await fg()).length>0}async function wa(e,t,{source:n="api"}={}){let r=await fg();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(t);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await xc())[e.id]=c,await Jr(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 kc(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 yc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await u_(e.id,c,"linked"),c}async function mg(e){let t=await xc();return t[e]?(delete t[e],await Jr(e),!0):!1}var dg=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function c_(e,t){if(!e.track?.cwd)return null;let n=Ir(await H({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function b4(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${dg(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${dg(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function k4(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(dg(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function kc(e,t){let n=await Yo(t);if(!n)return;let r=await c_(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Ed(e),title:`Bostrat Track: ${t.track.name}`,subtitle:k4(t,r)})}function _d(e){let t=new URL(s_());return`${t.protocol}//${e}.${t.host}/`}var x4={linked:"linked",opened:"prOpened",merged:"prMerged"};function v4(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 l_(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Jr(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 u_(e,t,n){let r=await Yo(t);if(!r)return;let o=r.cfg.ticketFlow?.[x4[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=pg(s,a),c=v4(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 l_(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:n_(),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 d_(e){let t=await Nn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Yo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=pg(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Jr(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 hg(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Nn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Yo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=pg(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 Jr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await l_(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function f_(e,t){let n=await Nn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Yo(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 p_(e,t,n){if(!t)throw new Error("offerId required");let r=await H({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 hg(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 XE=50;async function m_(){let e=await Go();if(!e)throw new Error("no ticketing integration configured");return ha(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>XE)throw new Error(`too many issues (${n.length}); cap is ${XE} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function T4(e,t,n,r){let o=r??await ha(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 Nn(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 h_(e,t){let n=await Go();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 ha(n),s=await T4(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=n_();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 w_(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await H({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 Go();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await ha(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=[],d=null;if(i.epic){let h=await rg(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await og(a,{teamId:c.id,title:h.title,description:h.description,projectId:d,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 f=l.filter(h=>h.kind==="issue"),p=null;return i.linkCurrentTrack&&f.length===1&&!await Nn(e.id)&&(p=await wa(e,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function yc(e,t){let n=await Yo(e);n&&await n.api.createComment(n.cfg,e,t)}async function A4(e,t,n){let r=await Yo(e);if(!r)return null;let o=await rr(t);if(!o)return null;let s=an(o,n);if(!s)return null;let a=await bc.readFile(Kt(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 ZE=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),cg=new Map;function E4(e,t){if(ZE()<=0)return kc(e,t);if(cg.has(e))return;let n=setTimeout(()=>{cg.delete(e),Nn(e).then(r=>r?kc(e,r):null).catch(()=>{})},ZE());n.unref?.(),cg.set(e,n)}var QE=new Map;async function e_(e,t,n){let r=QE.get(e)??0;Date.now()-r<600*1e3||(QE.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 _4(e,{resolveTrack:t=null}={}){let n=await Nn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await fg())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 wa(s,o,{source:"chat"})}catch(a){await e_(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 Jr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Jr(e.trackId),await kc(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 Jr(e.trackId),await kc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await c_(n,e.trackId).catch(()=>null):null;await yc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
679
692
|
|
|
680
|
-
Agent cost for this Track: ${
|
|
693
|
+
Agent cost for this Track: ${b4(o)}.`:"")),await u_(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await A4(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await yc(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
681
694
|
|
|
682
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
683
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
684
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
695
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await yc(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 yc(n,`**Bostrat** \u2014 live preview **${r.name}**: ${_d(r.slug)}
|
|
696
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await E4(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await e_(e.trackId,s?.cwd??null,o)}}function g_({resolveTrack:e}){rt.on("event",t=>{_4(t,{resolveTrack:e}).catch(()=>{})})}async function y_(){if(!je())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await xc(),r=0;for(let[o,s]of Object.entries(n))await wt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}To();ea();import I4 from"ws";var b_=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Id(e,t,n={}){let r=await fetch(`${b_()}/${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 wg=(e,t)=>Id("chat.postMessage",e,t),Jo=(e,t)=>Id("chat.update",e,t);async function bt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function $4(e,t,n={}){let r=await fetch(`${b_()}/${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 k_(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await $4("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 Id("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var R4=2200;function x_({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 Id("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new I4(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let f=w=>{try{o.send(JSON.stringify(w?{envelope_id:d.envelope_id,payload:w}:{envelope_id:d.envelope_id}))}catch{}},p=!1,h=setTimeout(()=>{p=!0,f()},R4);Promise.resolve().then(()=>t(d)).then(w=>{clearTimeout(h),!p&&(p=!0,f(d.accepts_response_payload?w:void 0))},w=>{clearTimeout(h),p||(p=!0,f()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var T_=()=>process.env.BOSTRAT_STATE_DIR||Rd.join(process.env.HOME||C4.tmpdir(),".bostrat"),xg=()=>Rd.join(T_(),"slack.json"),$d=null,gg=null,yg=Promise.resolve();async function Sg(){return $d||(gg??=vc.readFile(xg(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>$d=e).finally(()=>gg=null),gg)}async function v_(){let e=JSON.stringify({threads:$d},null,2),t=`${xg()}.${process.pid}.tmp`;await vc.mkdir(T_(),{recursive:!0}),await vc.writeFile(t,e),await vc.rename(t,xg())}async function dn(){yg=yg.then(v_,v_),await yg.catch(()=>{})}async function Tg(e){return(await Sg())[e]??null}async function M4(e,t){let n=await Sg();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 Sc(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function N4(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Sc(n.replace(/\n+/g," "),t)}function A_(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(Sc(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
697
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Ed(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function vg(e,t){await Jo(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:A_(t)})}async function Zr(e,t,n,r=void 0){return wg(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var S_=new Set;function Xr(e){S_.add(e),e.catch(()=>{}).finally(()=>S_.delete(e))}var De=e=>({response_type:"ephemeral",text:e}),P4="*/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._",D4=["claude","cursor","codex"];function bg(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 kg=(e,t)=>De(`No live Track matches \u201C${Se(t)}\u201D. Live: ${e.map(n=>`\`${Se(n.name)}\``).join(" \xB7 ")||"none"}`);async function E_(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=t.maxConcurrent){await bt(o,{...De(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await bt(o,{...De("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?Rd.basename(c.base):"fresh workspace"),lead:"claude"},u=await wg(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:A_(l),unfurl_links:!1});l.ts=u.ts,(await Sg())[i.id]=l,await dn(),await bt(o,{...De(`\u2713 *${Se(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await bt(o,{...De(`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 Tg(n.id);s&&(s.archivedAt=Date.now(),await dn(),await vg(t,s).catch(()=>{})),await bt(r,{...De(`\u2713 *${Se(n.name)}* archived.`),replace_original:!0})}catch(s){await bt(r,{...De(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function j4(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await bt(o,{...De(`\u2713 Handing *${Se(n.name)}* to ${Se(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await bt(o,{...De(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function L4(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 bt(r,{...De(`\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 bt(r,{...De(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function F4(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Sc(`${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 U4(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?Rd.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(`
|
|
685
698
|
`)+(o?`
|
|
686
|
-
_${o} archived_`:"")}async function
|
|
687
|
-
\u2713 *${Se(a)}*`}}]}).catch(()=>{})}}catch(c){await
|
|
688
|
-
\u2713 _answered_`}}]})}async function
|
|
689
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
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
|
|
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
|
-
`)}
|
|
693
|
-
`).slice(-3).join(" | ").slice(0,300);
|
|
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
|
|
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
|
|
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
|
|
697
|
-
`,{mode:384}),
|
|
699
|
+
_${o} archived_`:"")}async function H4(e,t){let n=await Vo();if(!n)return De("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 De("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(Xr(E_(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),De(`Starting *${Se(u)}* on ${Se(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:F4(u,i)}}case"archive":{if(!a.length)return De("Usage: `/bostrat archive <track>`");let{match:i,live:c}=bg(await r.listTracks(),a.join(" "));return i?(Xr(B4(e,n,{track:i,responseUrl:t.response_url})),De(`Archiving *${Se(i.name)}*\u2026`)):kg(c,a.join(" "))}case"handoff":{let i=r.knownAgents??D4,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return De(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=bg(await r.listTracks(),l);return u?(Xr(j4(e,n,{track:u,agent:c,responseUrl:t.response_url})),De(`Handing *${Se(u.name)}* to ${Se(c)}\u2026`)):kg(d,l)}case"stop":{if(!a.length)return De("Usage: `/bostrat stop <track>`");let{match:i,live:c}=bg(await r.listTracks(),a.join(" "));return i?(Xr(L4(e,n,{track:i,responseUrl:t.response_url})),De(`Stopping *${Se(i.name)}*\u2026`)):kg(c,a.join(" "))}case"list":return De(await U4(r));default:return De(P4)}}async function W4(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Tg(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 dn(),await Jo(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Se(l||"question")}~
|
|
700
|
+
\u2713 *${Se(a)}*`}}]}).catch(()=>{})}}catch(c){await bt(n.response_url,De(`Couldn't answer: ${c?.message||c}`))}}async function q4(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(f=>f.type==="pr.offered"&&f.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===s))throw new Error("offer already answered");let d=Array.isArray(r.actions)?Mr.filter(f=>r.actions.includes(f)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(d?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:d}))return;d&&await i.sayToLead(c.id,Fs(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await bt(n.response_url,De(`Couldn't act on the offer: ${c?.message||c}`))}}async function K4(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 bt(n.response_url,De(`Couldn't act on the offer: ${i?.message||i}`))}}async function G4(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Vo();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 bt(t.response_url,{...De(`Starting *${Se(s)}*\u2026`),replace_original:!0}),Xr(E_(e,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:t.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?Xr(W4(e,r,t,o)):n.action_id==="bostrat-ship"?Xr(q4(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&Xr(K4(e,r,t,o))}async function V4(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 M4(t.channel,t.thread_ts);if(!n)return;let r=await Vo();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Zr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function z4(e){return async t=>{if(t.type==="slash_commands")return H4(e,t.payload);if(t.type==="interactive"){await G4(e,t.payload);return}if(t.type==="events_api"){await V4(e,t.payload?.event);return}}}var Y4=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 J4(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:Sc(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 X4(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await Jo(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 Zr(e,t,r.question||"The lead needs an answer",J4(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await dn()}async function Z4(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 dn(),await Jo(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Se(s||"question")}~
|
|
701
|
+
\u2713 _answered_`}}]})}async function Q4(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(d=>l[d]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await Zr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Se(Sc(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await dn()}async function e2(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await dn();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 Jo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function t2(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 Zr(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 dn()}async function n2(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await dn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Jo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function r2(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??o2)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await k_(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 Zr(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 o2(e,t){let n=await rr(e);if(!n)return null;let r=an(n,t);if(!r)return null;let o=await vc.readFile(Kt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function s2(e,t){if(!Y4.has(t.type))return;let n=await Tg(t.trackId);if(!n)return;let r=await Vo();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 dn(),await vg(r,n);return;case"turn.agent":{if(!o.text)return;let s=N4(o.text);if(!s)return;await Zr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Se(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Ed(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return X4(r,n,t);case"prompt.answered":return Z4(r,n,t);case"pr.offered":return Q4(r,n,t);case"pr.offer.answered":return e2(r,n,t);case"pr.exists.offered":return t2(r,n,t);case"pr.exists.answered":return n2(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 Zr(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 dn(),await vg(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Zr(r,n,`Live preview ${o.name}: ${_d(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Se(o.name)}*: ${_d(o.slug)}
|
|
702
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return r2(r,n,t,e.deps)}}function __(e){let t={deps:e};rt.on("event",s=>{s2(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Vo()||(r&&clearInterval(r),r=null,n=x_({getToken:async()=>(await Vo())?.appToken,onEnvelope:z4(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}nn();me();wn();Km();Wn();import a2 from"node:fs/promises";function i2(e){let t=new Map;for(let n of String(e||"").split(`
|
|
703
|
+
`)){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 c2(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 l2=new Set(["Write","Edit","NotebookEdit"]),u2=220,d2=160;function f2(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(l2.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,u2)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=io(o.command.trim().replace(/\s+/g," ")).slice(0,d2));return r}function p2(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?tn(t.cwd,t.sessionId):t?.cwd?qf(t.cwd):null}async function I_(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:f2(e,o)});break;case"Stop":await m2(r,o,e,i);break;default:break}return a}async function m2(e,t,n,r){let o=[],s=p2(n,t);if(s)try{o=i2(await a2.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=c2(await H(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=`${d.model||"unknown"}\0${d.speed||""}`,p=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let h of Object.keys(p.tokens))p.tokens[h]+=d.tokens[h];d.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=d.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=d.cacheWriteTtl.h1),p.messageIds.push(d.messageId),p.speed=d.speed||null,u.set(f,p)}for(let[d,f]of u){let p=d.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:kn(p,f.tokens,void 0,f.speed?{speed:f.speed}:void 0),tokens:f.tokens,...f.speed?{speed:f.speed}:{},cacheWriteTtl:f.cacheWriteTtl,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}rs();Df();_u();me();me();$r();me();$r();Wn();function Tc(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 $_=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),R_=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Od=e=>Math.round(e*1e4)/1e4;async function Ag(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=$_(),d=0,f=[];for(let p of e||[]){let h=await H(p),w=Ir(h,{boxLabels:Tc(p)});if(w.turns===0){p.archivedAt?d++:f.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let g of w.unpricedModels||[])l.add(g);R_(u,w.totalTokens);for(let[g,k]of Object.entries(w.byModel))n[g]||={usd:0,events:0,tokens:$_()},n[g].usd=Od(n[g].usd+k.usd),n[g].events+=k.events,R_(n[g].tokens,k.tokens);for(let[g,k]of Object.entries(w.byProvider))r[g]||={usd:0,events:0},r[g].usd=Od(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:Is(),fleet:{totalUsd:Od(o),asRecordedUsd:Od(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:d+f.length,tracksSilentArchived:d,tracksSilentLive:f},tracks:t}}var Qr=e=>`$${e.toFixed(4)}`,ga=(e,t)=>String(e).padEnd(t);function O_(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 ${Qr(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 ${Qr(t.asRecordedUsd)} \xB7 current-rates total ${Qr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Qr(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(` ${ga(a,26)} ${ga(Qr(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(` ${ga(a.name||a.id,30)} ${Qr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ga(c.label,40)} ${ga(Qr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${ga(Qr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
704
|
+
`)}me();mm();import x2 from"node:fs/promises";import v2 from"node:path";Ol();Dl();$r();var Cd=e=>e.payload?.ts??e.ts,h2=12e4,zt=e=>Math.round(e*1e4)/1e4,w2=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function g2(e,{since:t}){let{track:n,events:r,brief:o}=e,s=y=>Cd(y)>=t,a=Hn([ui(n?.name),ui(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let y of r)if(y.type==="subtask.delegated"&&y.boxId&&y.payload?.task){let _=Hn(y.payload.task);_&&i.set(y.boxId,_)}let c=new Map,l=y=>{let _=y||"unattributed";return c.has(_)||c.set(_,{segments:[u(null)],lastTs:null,provider:null}),c.get(_)},u=(y,{shape:_="initial",continuation:M=!1,text:j=null,ts:L=null}={})=>({textCat:y,shape:_,continuation:M,text:j,ts:L,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((y,_)=>Cd(y)-Cd(_)),f=0,p=0,h=0,w={turns:0};for(let y of d){let _=l(y.boxId),M=_.segments[_.segments.length-1],j=Cd(y);switch(_.lastTs!=null&&j>_.lastTs&&s(y)&&(M.activeMs+=Math.min(j-_.lastTs,h2)),_.lastTs=j,y.provider&&(_.provider=y.provider),y.type){case"turn.user":{let L=y.payload?.text;if(y.source==="hook"||!String(L||"").trim()||y.payload?.note||_s(L))break;let F=Hn(L),A=!!y.payload?.kind,I=A||Ik(L),C=u(F,{shape:A?"injected":I?"follow-up":"novel-ask",continuation:!F&&I,text:M_(L),ts:j});_.segments.push(C),s(y)&&(C.turns+=1);break}case"peer.message":{let L=y.payload?.text;if(!String(L||"").trim())break;let F=Hn(L),A=u(F,{shape:"peer",continuation:!F,text:M_(L),ts:j});_.segments.push(A),s(y)&&(A.turns+=1);break}case"tool.called":{if(y.source==="transcript")break;let L=y.payload?.tool;L&&(M.tools[L]=(M.tools[L]||0)+1),s(y)&&(M.toolCalls+=1);break}case"cost.recorded":{if(!s(y))break;let{usd:L,tokens:F,model:A,unpriced:I}=Ut(y);I&&(w.turns+=1),M.usd+=L,F&&(M.tokens.input+=F.input,M.tokens.output+=F.output,M.tokens.cacheRead+=F.cacheRead,M.tokens.cacheWrite+=F.cacheWrite),M.models[A]=(M.models[A]||0)+L,y.provider&&(M.provider=y.provider);break}case"handoff.done":s(y)&&(f+=1);break;case"subtask.merged":s(y)&&(p+=1);break;case"error":s(y)&&y.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let g=new Map,k={usd:0,turns:0,byShape:{},samples:[]},x=!1;for(let[y,_]of c){let M=null;for(let j of _.segments){let L=null,F=null;if(j.textCat)L=j.textCat.category,F="text";else if(i.get(y))L=i.get(y).category,F="boxTask";else if(j.continuation&&M)L=M,F="inherited";else{let q=_k(j.tools);q?(L=q.category,F="toolMix"):a&&(L=a.category,F="hint")}if(L&&(M=L),!(j.usd>0||j.activeMs>0||j.turns>0||j.toolCalls>0))continue;let I=L??"uncategorized";g.has(I)||g.set(I,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let C=g.get(I);C.usd+=j.usd,C.activeMs+=j.activeMs,C.turns+=j.turns,C.toolCalls+=j.toolCalls,F&&(C.provenance[F]=(C.provenance[F]||0)+j.usd);for(let[q,R]of Object.entries(j.models))C.models[q]=(C.models[q]||0)+R;let J=j.provider||_.provider;J&&(C.providers[J]=(C.providers[J]||0)+j.usd),J==="cursor"&&j.usd===0&&(x=!0),L||(k.usd+=j.usd,k.turns+=j.turns,k.byShape[j.shape]=(k.byShape[j.shape]||0)+j.usd,j.usd>0&&j.text&&k.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:j.shape,usd:zt(j.usd),ts:j.ts,text:j.text}))}}return{perCategory:g,uncategorized:k,handoffs:f,merged:p,failed:h,sawCursorActivity:x,unpricedTurns:w.turns}}function Eg(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 k of e||[]){let{track:x,events:y}=k;if(!y?.length){a+=1;continue}let{perCategory:_,uncategorized:M,handoffs:j,merged:L,failed:F,sawCursorActivity:A,unpricedTurns:I}=g2(k,{since:r});A&&(i=!0),c+=I,l.usd+=M.usd,l.turns+=M.turns;for(let[J,q]of Object.entries(M.byShape))l.byShape[J]=(l.byShape[J]||0)+q;l.samples.push(...M.samples);let C={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:j,subtasksMerged:L,subtasksFailed:F};for(let[J,q]of _){C.usd+=q.usd,C.activeMs+=q.activeMs,C.turns+=q.turns,C.categories[J]={usd:zt(q.usd),activeMs:q.activeMs};for(let[X,G]of Object.entries(q.models))C.models[X]=zt((C.models[X]||0)+G);for(let[X,G]of Object.entries(q.providers))C.providers[X]=zt((C.providers[X]||0)+G);o.has(J)||o.set(J,w2());let R=o.get(J);R.usd+=q.usd,R.activeMs+=q.activeMs,R.turns+=q.turns,R.toolCalls+=q.toolCalls;for(let[X,G]of Object.entries(q.models))R.models[X]=(R.models[X]||0)+G;for(let[X,G]of Object.entries(q.providers))R.providers[X]=(R.providers[X]||0)+G;for(let[X,G]of Object.entries(q.provenance))R.provenance[X]=(R.provenance[X]||0)+G;let W=R.byTrack.get(x?.id)||{usd:0,activeMs:0};W.usd+=q.usd,W.activeMs+=q.activeMs,R.byTrack.set(x?.id,W)}(C.usd>0||C.activeMs>0||C.turns>0)&&(C.usd=zt(C.usd),C.dominantCategory=k2(C.categories),s.push(C))}let u=[...o.values()].reduce((k,x)=>k+x.usd,0),d=[...o.values()].reduce((k,x)=>k+x.activeMs,0),f=u>0?"usd":"time",p=f==="usd"?u:d,h=new Map((e||[]).map(k=>[k.track?.id,k.track?.name||k.track?.id])),w=[...o.entries()].map(([k,x])=>({category:k,sharePct:p>0?Math.round((f==="usd"?x.usd:x.activeMs)/p*100):0,shareBasis:f,usd:zt(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:Ac(x.models),providers:Ac(x.providers),provenance:Ac(x.provenance),topWorkstreams:[...x.byTrack.entries()].sort((y,_)=>_[1].usd-y[1].usd||_[1].activeMs-y[1].activeMs).slice(0,2).map(([y,_])=>({trackId:y,name:h.get(y),usd:zt(_.usd),activeMs:_.activeMs}))})).sort((k,x)=>x.usd-k.usd||x.activeMs-k.activeMs);s.sort((k,x)=>x.usd-k.usd||x.activeMs-k.activeMs);let g={};for(let k of o.values())for(let[x,y]of Object.entries(k.provenance))g[x]=(g[x]||0)+y;return{since:r,capturedAt:n,totalUsd:zt(u),activeMs:d,turns:[...o.values()].reduce((k,x)=>k+x.turns,0),bullets:w,workstreams:s,provenance:Ac(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:zt(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Ac(l.byShape),top:l.samples.sort((k,x)=>x.usd-k.usd).slice(0,y2)}}}}var y2=25,C_=160,M_=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>C_?`${t.slice(0,C_).trimEnd()}\u2026`:t},b2=10;function N_(e,{thresholdPct:t=b2}={}){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:zt(o),totalUsd:zt(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var k2=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},Ac=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,zt(n)]));var Zo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function S2(e){let t=String(e||"7d");return t in Zo?{key:t,ms:Zo[t]}:{key:"7d",ms:Zo["7d"]}}function T2(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=nu(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 A2(e){if(!e?.cwd)return null;try{let n=(await x2.readFile(v2.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 _g(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),d=[];for(let h of e||[]){let w=await H(h),g=T2(h,w);i&&c<o&&(g=g.filter(k=>(k.ts??0)<=c)),d.push({track:h,events:g,brief:await A2(h)})}let f=Eg(d,{windowMs:u,now:c}),p=l==="7d"&&c===o?f:Eg(d,{windowMs:Zo["7d"],now:o});return{window:l,...f,categorization:N_(p)}}var Ec=e=>`$${Number(e||0).toFixed(2)}`,P_=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},Xo=(e,t)=>String(e).padEnd(t);function D_(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${Ec(e.totalUsd)} \xB7 ${P_(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(` ${Xo(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${Xo(a.category,15)} ${Xo(Ec(a.usd),10)} ${Xo(P_(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 ${Ec(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} ${Ec(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${Xo(Ec(a.usd),8)} ${Xo(a.shape,10)} ${Xo(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(`
|
|
705
|
+
`)}Wn();var eo=36e5,F_=24*eo,E2=400,_2=12,Qo=e=>Math.round(e*1e4)/1e4;function Ig({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 Zo?String(e||"7d"):"7d",i=Zo[a];return{key:a,since:i==null?null:r-i,until:r}}var I2=(e,t)=>e!=null&&t-e<=48*eo?eo:F_;function B_(e,t){if(t===eo)return Math.floor(e/eo)*eo;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function $2(e,t){if(t===eo)return e+eo;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var j_=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),L_=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},R2=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Qo(n)]));async function U_(e,t={}){let n=t.now??Date.now(),r=Ig({...t,now:n}),o=r.since==null?F_:I2(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=j_(),d=0,f=0,p=0,h=0,w=0,g=0,k=new Set,x=new Map,y=[],_=[],M=[],j=null;for(let A of e||[]){let C=(await H(A)).filter(R=>R.type==="cost.recorded");if(!C.length){A.archivedAt||M.push(A.name||A.id);continue}let J=[];for(let R of C){let W=R.ts??0;s(W)?J.push(R):i(W)&&(g+=Ut(R).usd)}if(!J.length)continue;let q=Ir(J,{boxLabels:Tc(A)});d+=q.totalUsd,f+=q.turns,p+=q.repricedTurns||0,h+=q.asRecordedUsd||0,w+=q.unpricedTurns||0;for(let R of q.unpricedModels||[])k.add(R);L_(u,q.totalTokens);for(let[R,W]of Object.entries(q.byModel))c[R]||={usd:0,events:0,tokens:j_()},c[R].usd=Qo(c[R].usd+W.usd),c[R].events+=W.events,L_(c[R].tokens,W.tokens);for(let[R,W]of Object.entries(q.byProvider))l[R]||={usd:0,events:0},l[R].usd=Qo(l[R].usd+W.usd),l[R].events+=W.events;for(let R of J){let{usd:W}=Ut(R);if(!W)continue;let X=B_(R.ts??0,o),G=x.get(X)??{t:X,total:0,byProvider:{}};G.total+=W;let Te=R.provider||"unknown";G.byProvider[Te]=(G.byProvider[Te]||0)+W,x.set(X,G),(j==null||X<j)&&(j=X)}y.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:q.totalUsd,turns:q.turns,providers:Object.keys(q.byProvider)});for(let[R,W]of Object.entries(q.byBox))_.push({trackId:A.id,trackName:A.name||A.id,boxId:R,...W})}let L=r.since!=null?B_(r.since,o):j,F=[];for(let A=L,I=0;A!=null&&A<r.until&&I<E2;A=$2(A,o),I++){let C=x.get(A);F.push(C?{t:A,total:Qo(C.total),byProvider:R2(C.byProvider)}:{t:A,total:0,byProvider:{}})}return y.sort((A,I)=>I.usd-A.usd),_.sort((A,I)=>I.usd-A.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Is(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:F,fleet:{totalUsd:Qo(d),asRecordedUsd:Qo(h),turns:f,repricedTurns:p,unpricedTurns:w,unpricedModels:[...k],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:y.length,tracksSilentLive:M},prior:a!=null?{totalUsd:Qo(g),since:r.since-a,until:r.since}:null,tracks:y.slice(0,50),boxes:_.slice(0,_2)}}Au();var O2=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),C2=20,M2=40;async function H_(e,t={}){let n=t.now??Date.now(),r=Ig({...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 H(l),d=0,f=Tc(l);for(let p of u){if(!O2.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"&&d++,s.push(p.trackId?p:{...p,trackId:l.id}))}d&&i++;for(let p of l.boxes||[])a[`${l.id}/${p.name}`]={trackName:l.name||l.id,label:f[p.name]||p.name}}let c=dS(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:Fr,decayEndDays:Pm,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,C2),fixes:c.fixes.slice(0,M2)}}import N2 from"node:fs";import W_ from"node:fs/promises";import _c from"node:os";import P2 from"node:path";vh();ut();var D2=1024*1024,Md=e=>Math.round(e/D2);async function B2(e){try{let t=await W_.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Md(r),diskTotalMb:Md(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function j2(){try{return(await Qt()).filter(t=>t.isBostrat).length}catch{return null}}async function L2(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await W_.readdir(P2.join(fc,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function q_({stateDir:e=fc,now:t=Date.now}={}){let n=_c.totalmem(),[r,o,s,a]=await Promise.all([Wu().catch(()=>null),B2(N2.existsSync(e)?e:_c.tmpdir()),j2(),L2()]),i=_c.cpus()?.length||null,c=_c.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?Md(n-r):null,memTotalMb:Md(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(_c.uptime())}}me();hp();ja();Wn();me();Wn();var F2=864e5;function U2(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=kn(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 K_(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*F2,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await H(u);for(let f of d){if(!f.ts||f.ts<o)continue;let p=U2(f,{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}}ah();Kh();Ql();lt();xn();import ya from"node:fs/promises";import H2 from"node:fs";import pt from"node:path";import W2 from"node:os";import{execFile as q2}from"node:child_process";import{promisify as K2}from"node:util";var G2=K2(q2),G_=process.env.BOSTRAT_STATE_DIR||pt.join(process.env.HOME||W2.tmpdir(),".bostrat"),Nd=!1,$g=null;async function Rg(e,t){let n=await ya.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ya.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ya.mkdir(pt.dirname(t),{recursive:!0}),await ya.copyFile(e,t),!0)}var V2=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 ya.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(V2.has(s.name)||n?.has(s.name))continue;let a=pt.join(e,s.name);s.isDirectory()?r+=await Pd(a,pt.join(t,s.name)):s.isFile()&&(r+=await Rg(a,pt.join(t,s.name))?1:0)}return r}async function V_({stateDir:e=G_,dest:t=process.env.BOSTRAT_SYNC_DIR||`${G_}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ya.mkdir(t,{recursive:!0});let o=0;o+=await Rg(pt.join(e,"tracks.json"),pt.join(t,"tracks.json"))?1:0,o+=await Rg(pt.join(e,"events.jsonl"),pt.join(t,"events.jsonl"))?1:0,o+=await Pd(pt.join(e,"events"),pt.join(t,"events")),o+=await Pd(pt.join(e,"memory"),pt.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Pd(pt.join(u.cwd,".bostrat"),pt.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>G2("git",["-C",t,...u]);H2.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 gf(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||Nd))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Nd&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Nd=!1,$g=null}catch(u){Nd=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
706
|
+
`).slice(-3).join(" | ").slice(0,300);d!==$g&&($g=d,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${d}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}Uh();nn();po();ut();Oa();import{execFile as z2}from"node:child_process";import{promisify as Y2}from"node:util";import{randomUUID as J2}from"node:crypto";import X2 from"node:fs";import Z2 from"node:os";import Q2 from"node:path";var no=Y2(z2),Dd=e=>new Promise(t=>setTimeout(t,e)),ro=Lf,Og=process.env.BOSTRAT_USAGE_PROBE_CWD||Q2.join(Z2.homedir(),".bostrat","usage-probe"),eH=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,tH=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,nH=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,z_=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,to=null,Ic=null,$c=null;async function rH(){try{return await no(Y,Z("has-session","-t",ro)),!0}catch{return!1}}function oH(){to&&clearTimeout(to),to=setTimeout(()=>sH().catch(()=>{}),eH),to.unref&&to.unref()}async function sH(){to&&(clearTimeout(to),to=null);try{await no(Y,Z("kill-session","-t",ro))}catch{}}async function aH(){let e=Date.now()+nH;for(;;){let t=await Cg().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Dd(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Dd(500)}}async function iH(){if(await rH())return;if(!await kr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");X2.mkdirSync(Og,{recursive:!0}),await fr(Og);let e=J2();await no(Y,Z("new-session","-d","-s",ro,"-c",Og,"-x","200","-y","60",...Va()));let t=`${jf} --session-id ${e}`;await no(Y,Z("send-keys","-t",ro,t,"Enter")),await aH()}async function Cg(){let{stdout:e}=await no(Y,Z("capture-pane","-t",ro,"-p"));return e}async function cH(){await iH(),await no(Y,Z("send-keys","-t",ro,"-l","/usage")),await no(Y,Z("send-keys","-t",ro,"Enter"));let e=Date.now()+tH,t="";for(;await Dd(400),t=await Cg(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Dd(900),t=await Cg(),await no(Y,Z("send-keys","-t",ro,"Escape")).catch(()=>{}),oH(),t}function lH(e){let t=String(e||"").split(`
|
|
707
|
+
`).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 uH(e){let t=String(e||"").split(`
|
|
708
|
+
`).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 Y_({fresh:e=!1}={}){return!e&&Ic&&Date.now()-Ic.at<z_?Ic.data:$c||($c=(async()=>{try{let t=await cH(),n=lH(t),r={...n,contributors:uH(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ic={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 Ic={at:Date.now()-(z_-3e3),data:n},n}finally{$c=null}})(),$c)}var dH=262144;function fH(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 pH=new Set([300]),mH=new Set([10080]);function hH(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:pH.has(t)?"session":mH.has(t)?"week":"other"}function wH(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 gH(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?fH((o-n)/1e3):null,resetsAt:o}}var Rc=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function yH(e,t=Date.now()){let n=String(e||"").split(`
|
|
709
|
+
`),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 Rc(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Rc(r);[c.primary,c.secondary].forEach((p,h)=>{let w=gH(p,l,t);if(!w)return;let g=hH(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:wH(p.window_minutes),...w})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let f=c.credits;return f&&(f.has_credits||f.unlimited)&&(u.credits={balance:String(f.balance??""),unlimited:!!f.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...Rc(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Rc(r)}return Rc(r)}async function Mg(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await uo(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 Tt(n.file,dH),o=yH(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 J_(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 Bd({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 Mg(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Mg(null))}return n.has("cursor")&&r.push(await Bd({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Bd(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?Mg(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Y_({fresh:t})}}mr();lt();lt();function bH(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}var kH=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),jd=(e,t)=>Object.assign(new Error(e),{status:t});async function X_(e,t){if(!bH(t))throw jd("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Re(t)}${e}`,{headers:kH(t)})}catch(o){throw jd(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw jd(r?.error||`plane replied ${n.status}`,n.status);return r}async function Z_(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await X_(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Q_(e,{env:t=process.env}={}){if(!e)throw jd("message id required",400);return(await X_(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}Si();pu();function Ng(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 xH(e){let[t,n]=e.split("/"),r=Ng(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 eI(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(xH).filter(Boolean)}function vH(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function tI(e){let t=eI(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:vH,allowed(r){if(n)return!0;let o=Ng((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function nI(e,t){let n=eI(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Ng(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import rI from"node:crypto";import Ld from"node:fs";import SH from"node:path";function oI(e=process.env,{stateDir:t=fc,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=Ld.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=rI.randomBytes(32).toString("base64url");try{Ld.mkdirSync(t,{recursive:!0}),Ld.writeFileSync(o,s+`
|
|
710
|
+
`,{mode:384}),Ld.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Oc(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 sI(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:rI.timingSafeEqual(n,r)}function aI(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 TH=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/,/^\/api\/tracks\/adopt\b/,/^\/api\/tracks\/[^/]+\/transfer-out\b/,/^\/api\/node\/self-update\b/,/^\/api\/changelog\/(publish|retract)\b/,/^\/api\/tracks\/[^/]+\/loop-(update|delete)-offer\b/],AH=e=>TH.some(t=>t.test(e));function iI({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=aI(o);return sI(c,e)?a():AH(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||Oc(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 cI(e,t,{enforce:n=!0}={}){return!n||sI(aI(e),t)?!0:Oc(e)}wn();Lu();Lu();import EH from"node:http";import _H from"node:net";function Fd(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 Ud(e){let t=Fd(e.headers.host);return t?{slug:t,view:yh(t)}:null}var IH=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),$H=e=>{let t=IH(e.name);return`<!doctype html><meta charset="utf-8">
|
|
698
711
|
<meta http-equiv="refresh" content="3">
|
|
699
712
|
<title>${t} \u2014 starting</title>
|
|
700
713
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
701
714
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
702
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
715
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function lI(e){if(!/[?&]token=/.test(e))return e;try{let t=new URL(e,"http://view");return t.searchParams.delete("token"),t.pathname+t.search}catch{return e}}function uI(e,t,n){let r=Ud(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($H(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,delete a["x-bostrat-token"];let i=EH.request({host:"127.0.0.1",port:o.port,method:e.method,path:lI(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 dI(e,t,n){let r=Ud(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
|
|
703
716
|
\r
|
|
704
717
|
bostrat: no live view "${s}"\r
|
|
705
|
-
`),t.destroy(),!0;let a=
|
|
718
|
+
`),t.destroy(),!0;let a=_H.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${lI(e.url)} HTTP/1.1`];for(let[l,u]of Object.entries(e.headers))l!=="host"&&(l==="authorization"||l==="cookie"||l==="x-bostrat-token"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
706
719
|
`)+`\r
|
|
707
720
|
\r
|
|
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-
|
|
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(`
|
|
721
|
+
`),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 fI(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}.${RH(n.host)}/`}function RH(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 Fd(t)&&yh(n[0])?n.slice(1).join("."):t}ea();Rh();zu();zm();me();import pI from"node:os";import{randomUUID as Dg}from"node:crypto";var OH=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||pI.hostname(),nodeName:e.BOSTRAT_NODE_NAME||pI.hostname()});var mI=32768,hI=120,wI=1e3,gI=new Set(["claude","codex","cursor"]),CH=new Set(["off","transition","nag"]),MH=[[0,59],[0,23],[1,31],[1,12],[0,6]];function NH(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=MH[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 fn=e=>String(e??"").trim(),es={name(e){let t=fn(e);if(!t)throw new Error("a loop needs a name");if(t.length>hI)throw new Error(`name is too long (${t.length}); cap is ${hI}`);return t},schedule(e){let t=fn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!NH(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=fn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>mI)throw new Error(`prompt is too long (${t.length} chars); cap is ${mI}`);return t},expectation(e){let t=fn(e)||null;if(t&&t.length>wI)throw new Error(`expectation is too long (${t.length}); cap is ${wI}`);return t},notify(e){let t=fn(e)||"off";if(!CH.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=fn(typeof t=="string"?t:t?.provider)||"claude";if(!gI.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...gI].join(", ")}`);let r=typeof t=="object"&&t&&fn(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 PH(e,t){let n=e&&typeof e=="object"?e:{},r=es.name(n.name),o=es.schedule(n.schedule),s=es.prompt(n.prompt),a=es.expectation(n.expectation),i=es.notify(n.notify),c=es.assignment(n.assignment),l=fn(t?.workspace?.repo)||fn(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:d}=OH();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function bI(e,t){let n=PH(t,e),r=Dg();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 kI(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await H({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 Pg=["name","schedule","prompt","expectation","notify","assignment","enabled"],DH=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function xI(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 vI(e,t){let n=fn(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=>!Pg.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: ${Pg.join(", ")}`)}let o={};for(let a of Pg){if(!(a in n))continue;let i=es[a](n[a]);(a==="assignment"?DH(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 SI(e,t,n){let r=xI(t),{patch:o,changed:s}=BH(n,r),a=Dg(),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 yI=600;async function TI(e,t,n){let r=fn(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>yI)throw new Error(`reason is too long (${r.length}); cap is ${yI}`);let o=xI(t),s=Dg(),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 AI=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await H({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 EI(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 AI(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 _I(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 AI(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}}Ou();me();yr();me();Vn();zu();var II=10,$I=200,jH=120,LH=24,OI=/^\d{4}-\d{2}-\d{2}$/;function Bg(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var FH=(e=Date.now())=>new Date(e).toISOString().slice(0,10),mt=(e,t="i")=>new RegExp(e,t),UH=[{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"}],HH=[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 WH({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of UH)if(o.test(n)||o.test(r))return s;return null}function RI(e){let t=String(e||"");for(let n of HH){let r=n.exec(t);if(r)return r[0].trim()}return null}function CI(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 qH=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function KH(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Ki(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(`
|
|
722
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,jH)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${LH}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=qH.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let d=RI(i);if(d)throw new Error(`bullet ${r.length+1} mentions "${d}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>$I)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${$I}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=RI(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>II)throw new Error(`${r.length} bullets \u2014 over the ${II}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function GH(e,{repos:t=Bg()}={}){let n=new Set(t);return(await Gn()).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:WH({title:r.title,headRef:r.headRef})}))}async function MI(e,t){let n=await GH(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function NI(e,{md:t,day:n=null}){let r=KH(t),o=n==null||n===""?FH():String(n);if(!OI.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 PI(e,{day:t}){let n=String(t||"");if(!OI.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}}$o();Vn();Oo();me();$o();lt();Qn();Vn();Si();_u();pu();$i();xn();import{execFile as oW}from"node:child_process";import{promisify as sW}from"node:util";var VH=new Set(["pull_request","check_suite","workflow_run","push"]),zH=/^[^/\s]+\/[^/\s]+$/,YH=500,jg=new Set,Lg=[],Hd=new Map,Yt={hourStart:0,byEvent:{},duplicates:0,rejected:0};function Fg(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==Yt.hourStart&&(Yt.hourStart=r,Yt.byEvent={},Yt.duplicates=0,Yt.rejected=0),t==="accepted"?Yt.byEvent[n]=(Yt.byEvent[n]||0)+1:Yt[t]+=1}function JH(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!VH.has(e.event)||typeof e.repo!="string"||!zH.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 DI(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=JH(e);if(!o)return Fg(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(jg.has(o.deliveryId))return Fg(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(jg.add(o.deliveryId),Lg.push(o.deliveryId);Lg.length>YH;)jg.delete(Lg.shift())}let s=o.repo.toLowerCase(),a=Hd.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Hd.set(s,a),Fg(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 XH(e,t=Date.now()){let n=Hd.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 BI=1440*6e4;function Ug(e,t=Date.now()){let n=XH(e,t);return n.covered&&n.ageMs<BI}function jI(e=Date.now()){let t={};for(let[n,r]of Hd)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<BI};return{hourStart:Yt.hourStart,byEvent:{...Yt.byEvent},duplicates:Yt.duplicates,rejected:Yt.rejected,repos:t}}me();Vn();mr();Qn();xn();function LI(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function ZH(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var QH=new Set(["merged","closed","lost"]);function eW(e,t){if(!e?.blocked)return null;let n=LI(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 tW(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function nW(e,t,n,r,o){return(await H(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 rW(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 Wd(e,t={}){let{now:n=Date.now(),slugOf:r=_e,lookup:o=st,note:s=kv,tracks:a=async()=>(await pe()).filter(d=>!Ge(d)),asked:i=nW,emitChange:c=rW}=t,l=[],u=null;for(let d of e??[]){if(!d?.prs?.length)continue;let f=await r(d.path).catch(()=>null);if(f)for(let p of d.prs)try{if(!ZH(p))continue;let h=await o(f,p.number).catch(()=>null);if(!h?.trackId||QH.has(h.state))continue;let{prev:w}=await s(f,p.number,{blocked:LI(p),sha:p.headSha??null,at:n});if(!w)continue;let g=eW(w,p);if(!g||tW(h,g.change))continue;u??=new Map((await a()).map(_=>[_.id,_]));let k=u.get(h.trackId);if(!k)continue;let x=p.headSha??null;if(await i(k,g.change,p.number,x,w.at||0))continue;let y={number:Number(p.number),url:p.url||h.url,title:p.title||h.title,branch:p.headRefName||h.headRef,repo:f,sha:x,delegated:!1,reason:g.reason};g.change==="ci-fixed"?y.passed=p.checks.passed:y.base=p.baseRefName||null,await c(k,g.change,y),l.push({change:g.change,number:p.number,reason:g.reason})}catch{}}return l}var qg=sW(oW),GI=3e4,aW=30*6e4,iW=GI,cW=10*6e4,lW=10080*6e4,uW=60*6e4,dW=8,VI=1440*6e4,fW=3,zI="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Wg=60*6e4,pW=15*6e4,mW=9e4,hW=1440*6e4,qd=new Map,Kd=new Map,wW=/could not resolve|not found|no pull requests?/i,gW=e=>wW.test(String(e?.message||e||""));function yW(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Wg)}var FI={fast:iW,slow:cW,decay:uW};function UI(e,t,n=Ug){return YI(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<aW||yW(e,t))?"fast":t-(e.firstSeen||0)>lW?"decay":"slow"}function Mc(e,t,n,r){let o=e.lanes?.get(oe(t.repo,t.number))||{};return fu({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function bW(e,t){Yn("watch");try{let{stdout:n}=await qg("gh",["pr","view",String(t),"--repo",e,"--json",zI],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await mn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Br(n)&&jr(n),n}}async function kW(e){Yn("watch");try{let{stdout:t}=await qg("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",zI],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await mn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Br(t)&&jr(t),t}}async function xW(){let e=new Map;for(let t of await pe().catch(()=>[]))e.set(t.id,t);return e}async function vW(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 Eu(e,t).catch(()=>{})}async function SW(e,t){let n=await Io(e);return n?qs(n,t):null}async function TW(e,t,n){let r=await Io(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ks(r,t,{method:n})}async function AW(e,t){let n=await Io(e);if(!n)throw new Error(`no local checkout for ${e}`);return Ei(n,t)}async function EW(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 ba(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 _W=10*6e4;async function IW(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>VI){await ba(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 ba(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await ba(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<_W)return;await ba(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 ba(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 ba(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await JI(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function ka(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 $W(e,t){try{return await qg("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Cc(e,t,n,r=()=>!0){return(await H(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var xa=(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 YI(e,t,{grace:n=mW}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=hW}async function RW(e,t,n,r){let o=r.now??Date.now();if(!YI(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ge(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??Ro)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await H(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??Kn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??Kn)(e.repo,e.number).catch(()=>null)?(await ka(s,"opened",{...xa(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 OW(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 ou(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await au(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<pW;await bv(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await yv(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Cc(a,"pushed",e.number,d=>d.payload?.sha===s)&&await ka(a,"pushed",{...xa(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Wg)await su(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 su(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Mc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Cc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await ka(a,"ci-fixed",{...xa(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await su(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Wg)await iu(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await iu(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Mc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Cc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await ka(a,"reconciled",{...xa(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await iu(e.repo,e.number).catch(()=>{})}}}}async function CW(e,t,n,r){let o=Mc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Cc(s,"closed",e.number)&&await ka(s,"closed",{...xa(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 MW(e,t,n){Mc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Cc(r,"lost",e.number)&&await ka(r,"lost",xa(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function JI(e,t,n,r,o={}){let s=Mc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await H(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 HI(e={}){let{now:t=Date.now(),auth:n=Sm,view:r=bW,list:o=kW,links:s=Sv,trackMap:a=xW,emitMerged:i=vW,mark:c=Pr,archiveIfArmed:l=Xn,notifyIfArmed:u=Zn,notifyIfResolved:d=id,followupsIfMerged:f=Wo,batchThreshold:p=fW,detail:h=SW,merge:w=TW,updateBranch:g=AW,lookup:k=st,disarm:x=vi,emitDisarmed:y=EW,localHasCommit:_=$W,claim:M=Kn,midTurn:j=Ro,only:L=null,wantDetail:F=null,covered:A=Ug,verdicts:I=null}=e;if(!(await n().catch(()=>null))?.authenticated||uu()>0)return 0;let J=await s().catch(()=>[]),q=Vv(J),R=V=>{try{return!!F?.(V)}catch{return!1}},W=L?J.filter(V=>{try{return L(V)}catch{return!1}}):J.filter(V=>t-(qd.get(oe(V.repo,V.number))||0)>=FI[UI(V,t,A)]);if(!W.length)return 0;let X=Date.now(),G=new Map,Te=new Map;for(let V of[...W].sort(_i)){let $e=L?"event":UI(V,t,A);G.set(oe(V.repo,V.number),{lane:$e,intervalMs:FI[$e]??null}),qd.set(oe(V.repo,V.number),t),Te.has(V.repo)||Te.set(V.repo,[]),Te.get(V.repo).push(V)}let le=await a().catch(()=>new Map),be=new Map,ae=(V,$e)=>{let Me=oe(V,$e);if(!be.has(Me)){let fe=Promise.resolve().then(()=>h(V,$e));fe.catch(()=>{}),be.set(Me,fe)}return be.get(Me)},K=I??((V,$e)=>Wd([{path:V,prs:[$e]}],{now:t,slugOf:async()=>V,tracks:async()=>[...le.values()].filter(Me=>!Ge(Me))}).catch(()=>{})),ue={now:t,lanes:G,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:f,detail:ae,merge:w,updateBranch:g,lookup:k,disarm:x,emitDisarmed:y,localHasCommit:_,claim:M,midTurn:j};for(let[V,$e]of Te){let Me,fe=new Set,E=P=>P.ciFixAwait||P.reconcileAwait||R(P)?!0:!P.autoMerge||P.trackId&&q.get(P.trackId)!==oe(P.repo,P.number)?!1:t-(P.autoMerge.armedAt||0)<=VI;if($e.length>p)Me=await o(V).catch(()=>null);else{Me=new Map;for(let P of $e){if(E(P)){let he=await ae(V,P.number).catch(()=>null);if(he?.state){Me.set(Number(P.number),{number:Number(P.number),url:he.url,title:he.title,headRefName:he.headRefName,headRefOid:he.headSha??null,state:he.state,mergedAt:he.mergedAt??null,closedAt:he.closedAt??null});continue}}try{let he=await r(V,P.number);he?.number!=null&&Me.set(Number(he.number),he)}catch(he){gW(he)&&fe.add(oe(P.repo,P.number))}}}if(Me)for(let P of $e){let he=oe(P.repo,P.number),Ze=Me.get(Number(P.number));if(!Ze){if(fe.has(he)){let Be=(Kd.get(he)||0)+1;Kd.set(he,Be),Be>=dW&&(await MW(P,le,ue),Kd.delete(he),qd.delete(he))}continue}if(Kd.delete(he),Ze.state==="MERGED"||Ze.state==="CLOSED")Ze.state==="MERGED"?await JI(P,Ze,le,ue):await CW(P,Ze,le,ue),qd.delete(he);else{await RW(P,Ze,le,ue).catch(()=>{}),await OW(P,Ze,le,ue).catch(()=>{});let Be=!P.trackId||q.get(P.trackId)===he;P.autoMerge&&Be&&await IW(P,le,ue).catch(()=>{});try{if(R(P)&&!be.has(he)&&ae(P.repo,P.number),be.has(he)){let Et=await be.get(he).catch(()=>null);Et?.checks&&await K(P.repo,Et)}}catch{}}}}let re={};for(let{lane:V}of G.values())re[V]=(re[V]||0)+1;return console.log(`[pr-watch] probed ${W.length} link(s) (${Object.entries(re).map(([V,$e])=>`${V} ${$e}`).join(", ")}) in ${Date.now()-X}ms`),W.length}function WI(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 Hg=!1,Gd=!1,Vd=[],qI=Promise.resolve();async function NW(){for(;Gd||Vd.length;){if(Gd){Gd=!1;try{await HI()}catch{}continue}let e=Vd;Vd=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await HI({...n,only:r=>t.some(o=>WI(o,r)),wantDetail:r=>t.some(o=>o.detail&&WI(o,r))})}catch{}}}function zd(e,t){return e===null?Gd=!0:Vd.push({spec:e,deps:t}),Hg||(Hg=!0,qI=NW().finally(()=>{Hg=!1})),qI}function XI(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 zd({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var PW=6e4,KI=0;function ZI(e=void 0){let t=e?.now??Date.now();return t-KI<PW?Promise.resolve():(KI=t,zd({all:!0},e))}function QI(){zd(null),setInterval(()=>zd(null),GI)}$o();Hm();Hu();Qn();xn();var DW=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function BW({tracks:e=pe,reconcile:t=Uu}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ge(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function e$(e,t={}){let{invalidate:n=Wt,probe:r=XI,poke:o=IS,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"&&DW.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();me();Qn();xn();import{randomUUID as jW}from"node:crypto";var LW=2e4,t$=3,va=new Map,Kg=new Set;async function n$(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 FW(e={}){let{sessions:t=null,tracks:n=null,append:r=$,read:o=H,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Qt(),i=n??await pe()}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 d=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let f=c.get(d.name);if(f)if(l.add(d.name),f.alternateOn){let p=(va.get(d.name)||0)+1;va.set(d.name,p),p===t$&&(Kg.add(d.name),await n$(u,d.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:jW(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(va.get(d.name)||0)>=t$;if(va.delete(d.name),p||!Kg.has(d.name)){Kg.add(d.name);let h=await n$(u,d.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...va.keys()])l.has(u)||va.delete(u)}var Nc=null,Gg=!1;function r$(){return Nc?()=>{}:(Nc=setInterval(()=>{Gg||(Gg=!0,FW().catch(()=>{}).finally(()=>{Gg=!1}))},LW),Nc.unref?.(),()=>{clearInterval(Nc),Nc=null})}To();Hu();me();$o();Oo();To();import{randomUUID as UW}from"node:crypto";async function HW(){let e=await Promise.resolve().then(()=>(Qn(),kE));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Vg={listRepoPrs:vn,readEvents:H,appendEvent:$,sayDeps:HW,currentBranch:Lr};async function WW(e,t=Vg,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Lr)(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 zg(e,t,{actions:n,notify:r=!1}={},o=Vg){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 WW(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=UW();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 Yg(e,{offerId:t,choice:n}={},r=Vg){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=Mr.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,Fs(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,iv(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 dr from"node:fs/promises";import Sa from"node:path";import u$ from"node:os";import o$ from"node:fs/promises";import Yd from"node:path";import{execFile as qW}from"node:child_process";import{promisify as KW}from"node:util";import{fileURLToPath as GW}from"node:url";var VW=KW(qW),a$=Yd.dirname(GW(import.meta.url)),zW=Yd.resolve(a$,"..","..","client","dist"),YW="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",JW=new Map([...YW].map((e,t)=>[e,t]));function XW(e){let t=[],n=0,r=0;for(let o of e){let s=JW.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 ZW(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=XW(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 QW(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 s$=new Map;async function eq(e){let t;try{t=await o$.stat(e)}catch{return null}let n=s$.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await o$.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:ZW(r.mappings||"")};return s$.set(e,o),o}catch{return null}}var Jg=null;function tq(){return Jg||(Jg=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):VW("git",["rev-parse","--short","HEAD"],{cwd:Yd.resolve(a$,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Jg}var nq=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function rq(e,{distDir:t=zW}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(nq)){let[a,i,c,l]=s,u=await eq(Yd.join(t,"assets",`${i}.map`)),d=u?QW(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function i$(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await tq();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await rq(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var oq=()=>process.env.BOSTRAT_STATE_DIR||Sa.join(process.env.HOME||u$.tmpdir(),".bostrat"),oo=()=>Sa.join(oq(),"telemetry"),d$=()=>Sa.join(oo(),".relay-offsets.json"),sq=50,aq=14,iq=3e4,c$=500,Bt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function cq(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 f$=e=>new Date(e).toISOString().slice(0,10),l$=0;async function p$(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,sq):[]).map(s=>cq(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await i$(s);await dr.mkdir(oo(),{recursive:!0});let o=Sa.join(oo(),`${f$(t)}.ndjson`);return await dr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
723
|
+
`).join(""),"utf8"),t-l$>6*3600*1e3&&(l$=t,lq(t).catch(()=>{})),r.length}async function lq(e=Date.now()){let t=f$(e-aq*24*3600*1e3),n=[];try{n=await dr.readdir(oo())}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 dr.rm(Sa.join(oo(),s),{force:!0});let o=await h$();for(let s of r)delete o[s];await w$(o)}var Jd=new Map;function m$(e,t=Date.now()){let n=e||"unknown",r=Jd.get(n);return r||(Jd.size>512&&Jd.clear(),r={tokens:40,at:t},Jd.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 h$(){try{return JSON.parse(await dr.readFile(d$(),"utf8"))||{}}catch{return{}}}async function w$(e){await dr.mkdir(oo(),{recursive:!0}),await dr.writeFile(d$(),JSON.stringify(e),"utf8")}function uq(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||u$.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 dq({fetchImpl:e=fetch}={}){let t=await GE();if(!t)return{sent:0,disabled:!0};let n;try{n=(await dr.readdir(oo())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await h$(),o=0;for(let s of n){let a=Sa.join(oo(),s),i=r[s]||0,c;try{let f=await dr.open(a,"r");try{let p=(await f.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await f.read(c,0,c.length,i)}finally{await f.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
724
|
+
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=c$){let p=d.slice(f,f+c$).map(w=>uq(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 w$(r)}return{sent:o,disabled:!1}}var Xg=null,Zg=!1;function g$(){Xg||(Xg=setInterval(async()=>{if(!Zg){Zg=!0;try{await dq()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Zg=!1}}},iq),Xg.unref?.())}import Zd from"node:fs/promises";import v$ from"node:path";import fq from"node:os";var S$=()=>process.env.BOSTRAT_STATE_DIR||v$.join(process.env.HOME||fq.tmpdir(),".bostrat"),T$=()=>v$.join(S$(),"dictation-glossary.json"),pq=100,mq=500,hq=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Qd=null,Qg=null,ey=Promise.resolve();function y$(){let e={},t=Date.now();for(let n of hq)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function A$(){return Qd||(Qg??=Zd.readFile(T$(),"utf8").then(e=>JSON.parse(e)?.terms??y$()).catch(()=>y$()).then(e=>Qd=e).finally(()=>Qg=null),Qg)}async function b$(){let e=JSON.stringify({terms:Qd},null,2),t=T$(),n=`${t}.${process.pid}.tmp`;await Zd.mkdir(S$(),{recursive:!0}),await Zd.writeFile(n,e),await Zd.rename(n,t)}function wq(){return ey=ey.then(b$,b$),ey.catch(()=>{})}async function ty(){return Object.values(await A$()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var gq=e=>Math.max(1,Math.ceil(e.length/4));async function yq(){let e=[],t=0;for(let{term:n}of await ty()){let r=gq(n);if(!(t+r>mq)&&(e.push(n),t+=r,e.length>=pq))break}return e}async function bq(e,t){let n=await A$(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var k$=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Xd=e=>e.toLowerCase();function x$(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function E$(e,t){let n=k$(e),r=k$(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=Xd(n[u])===Xd(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)Xd(n[c])===Xd(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(x$(r[l])&&i.add(r[l]),l++);for(;l<s;l++)x$(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await bq(u,"learned");return await wq(),[...i]}var Pc=null;function kq(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 _$(){if(!Pc)return null;let e=await gc();if(!e||e.apiKey.slice(-6)!==Pc.keyTail)return null;let{keyTail:t,...n}=Pc;return n}async function I$(e,t){let n=await gc();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 yq())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=kq(o.status,i);c.persistent&&(Pc={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 Pc=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}me();var ny={"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 xq(e){if(!e)return null;let t=String(e).trim();if(ny[t])return t;let n=null;for(let r of Object.keys(ny))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function vq(e,t={}){let n=ny[xq(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 Dc=null;async function Tq(){if(!Dc)return null;let e=await Ad();if(!e||e.apiKey.slice(-6)!==Dc.keyTail)return null;let{keyTail:t,...n}=Dc;return n}async function Aq(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&&(Dc={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}Dc=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 Eq={openai:Aq};async function $$(){let e=await Ad();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await Tq()}:{configured:!1}}async function R$(){let e=await Ad();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 Eq[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function O$(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=vq(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}}xn();var L$=Ae.dirname(Iq(import.meta.url)),C$=Number(process.env.PORT)||3362,tf=Kw(process.env),F$=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Ta=tI(process.env.BOSTRAT_ALLOW_IPS),iy=nI(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Ud),cy=oI(),U$=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"),Fd(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(Oc(e)||Ta.allowed(Ta.clientIp(e))||iy.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(uI);S.use(iI({token:cy,enforce:U$}));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 kr(),autoTriggerPct:F$,plane:Aa?Aa.status():null})});S.get("/api/sessions",async(e,t)=>{t.json({sessions:await Qt()})});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 xr(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 xe(n))return t.status(404).json({error:"session not found"});try{await en(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 xe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Za(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 S0(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=Ul(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 M$=10;S.get("/api/idle-guard",async(e,t)=>{try{t.json(qw({tracks:await pe(),views:await ju(),queuedOf:n=>Lo(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Jt=Ko(),Nq=NE({kind:Jt}),Bc=null,kt=()=>Bc&&["auto","prompt","off"].includes(Bc)?Bc:Nq,ay=null,Pq=hc.has(Jt),H$=async({to:e,from:t})=>!Pq||yd(await wc())?!1:(Lc.arm({to:e,from:t,kind:Jt}),!0);async function Dq(e){let t=kt();Bc=e??null;let n=kt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Lc.cancel();return}ay&&await H$(ay)}}var W$=(()=>{try{return Mq(import.meta.url)("../package.json").version||null}catch{return null}})(),Lc=DE({isBusy:async()=>qw({tracks:await pe(),views:await ju(),queuedOf:e=>Lo(e)}).busy,onStuck:({to:e,from:t})=>Cn({current:t,latest:e,installKind:Jt,policy:kt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Cn({current:t,latest:e,installKind:Jt,policy:kt(),status:"updating",reason:"auto"}):Cn({current:t,latest:e,installKind:Jt,policy:kt(),status:"failed",error:n?.reason})).catch(()=>{})});S.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Jt,policy:kt(),policySource:Bc?"plane":"node",current:W$,status:await wc(),armed:Lc.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 bd({to:n,kind:Jt,reason:"manual"});if(r.ok)return Lc.cancel(),Cn({current:r.from,latest:n,installKind:Jt,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 q$(){try{let e=await PE();e&&await Cn({current:e.from,latest:e.to,installKind:e.kind||Jt,policy:kt(),status:e.status,error:e.error,reason:e.reason}),await lw({current:W$,installKind:Jt,policy:kt()})}catch{}}q$();setInterval(q$,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await pe()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>M$){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,M$).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:oc,agentAvailability:await td(),tickets:await a_()})});S.get("/api/cost",async(e,t)=>{try{let n=await pe();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Ag(n);if(r.cursorUsage=sh(),e.query.format==="text")return t.type("text/plain").send(O_(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 _g(await pe(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(D_(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 U_(await pe(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=sh(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=bi(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(bi())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await Zl(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 J_(await pe(),{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 xy(e.get(Bn),{byToken:ac,legacy:()=>Di(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ra("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ra("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&&Ra("mismatch",{track:r.track.id,box:r.box.name}),Ra(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 I_(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&sl(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 ac(e.get(Bn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await pS(n.track,n.box)).join(`
|
|
712
725
|
`)+`
|
|
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
|
-
${
|
|
726
|
+
`)}catch{t.status(204).end()}});S.post("/api/git/committed",async(e,t)=>{try{let n=await ac(e.get(Bn));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?Ae.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Ae.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Ae.sep))?s:o;mS(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 se(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 wS(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 se(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await gS(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 H_(await pe(),{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(!m$(Ta.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await p$(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 jn(),r=new Set((await Promise.all(n.map(a=>_e(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Na()).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 Jc(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:Yc(),calls:Mv(),latency:Bv(),webhook:jI()})});S.post("/api/github/event",jt.json({limit:"64kb"}),(e,t)=>{let n=DI(e.body,{onAccept:r=>e$(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)=>{Oy(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await Z_(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 Q_(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function ly(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 jc({repo:e=null,number:t=null,branch:n=null}){let r=e?await _e(e):null;if(r&&t!=null){let s=await st(r,t),a=s&&await se(s.trackId);if(a)return a}if(!n)return null;let o=await pe();return bu(o,n,s=>ly(s,e,r))}async function K$({repo:e=null,number:t=null,branch:n=null}){let r=await jc({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ge(r))return r;if(!n)return null;let o=e?await _e(e).catch(()=>null):null,s=await pe();return bu(s,n,a=>!Ge(a)&&ly(a,e,o))}async function pn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await jc({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await _e(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 Bq(e){let t=(await pe()).filter(n=>!Ge(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await _e(n.path);if(r)for(let o of n.prs){if(await st(r,o.number))continue;let s=await bu(t,o.headRefName,a=>ly(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 qv();await Bq(n.repos).catch(()=>{}),await Wd(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{},f=null,p=null,h=null;try{if(s){let x=o||"merge";if(!Ai.has(x))throw new Error(`invalid merge method: ${x}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let _=await _e((await Jn(n)).path);if(!_)throw new Error("repo has no GitHub remote");let M=await jc({repo:n,number:y,branch:l}).catch(()=>null);if(!M)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Ei(n,y),await Rt({repo:_,number:y,trackId:M.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await xi(_,y,{merge:{method:x}}))throw new Error("couldn't record the auto-merge arm");return await Ao(_,y,i===!0),await Eo(_,y,c===!0&&i!==!0),Wt(),await pn("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:M,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}f=await jc({repo:n,number:r,branch:l}).catch(()=>null),f&&(p=await Wh(f,"merge",{count:1,summary:`merging PR #${r}`}));let w=await Ks(n,r,{method:o||"squash"});await pn("merged",{number:w.number,url:d,title:u,branch:l,repo:n});let g=await _e(n).catch(()=>null),k=!1;if(g&&(i===!0&&await Ao(g,w.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Eo(g,w.number,!0).catch(()=>{}),await Zn(g,w.number).catch(()=>{}),await Wo(g,w.number).catch(()=>{}),k=await Xn(g,w.number).catch(()=>!1),await Pr(g,w.number,"merged").catch(()=>{})),i===!0&&!k){let x=await jc({repo:n,number:w.number,branch:l}).catch(()=>null);x&&!x.archivedAt&&await Ho(x.id).catch(()=>{})}t.json(w)}catch(w){h=w;let g=w?.message||String(w),k=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(g)?400:502;t.status(k).json({error:g})}finally{f&&p&&await qh(f,p,h?"failed":"completed",h).catch(()=>{})}});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 _e((await Jn(n)).path).catch(()=>null),l=c?await vi(c,i).catch(()=>null):null;try{await Em(n,i)}catch(u){if(!l)throw u}Wt(),await pn("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 _e(n).catch(()=>null),s=await Yv(n,{method:r||"merge",onMerged:a=>{pn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Zn(o,a.number).catch(()=>{}).then(()=>Wo(o,a.number)).catch(()=>{}).then(()=>Xn(o,a.number)).catch(()=>{}).then(()=>Pr(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:zv()})});S.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{},i=null,c=null,l=null;try{let u=a||"merge";if(!Ai.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await se(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let f=(await Gn()).filter(w=>w.trackId===d.id&&w.state!=="merged"&&w.state!=="closed").sort(_i);if(!f.length)return t.status(409).json({error:"no open PRs on this Track"});c=await Wh(d,"merge",{count:f.length,summary:f.length===1?"merging 1 PR":`merging ${f.length} PRs`});for(let w of f)await Ao(w.repo,w.number,o===!0).catch(()=>{}),await Eo(w.repo,w.number,s===!0&&o!==!0).catch(()=>{});let p=[],h=[];for(let w of f){let g={repo:w.repo,number:w.number,ok:!0,action:"armed"};p.push(g);try{let k=await Io(w.repo);if(!k)throw new Error(`no local checkout for ${w.repo}`);let x=await qs(k,w.number);h.push({r:g,l:w,repoPath:k,pr:x,plan:Kv(x,{update:r!==!1})})}catch(k){g.ok=!1,g.action="failed",g.reason=k?.message||String(k)}}for(let{r:w,l:g,repoPath:k,pr:x,plan:y}of Gv(h))try{if(y.action==="skip")w.action="skipped",w.reason=y.reason;else if(y.action==="merge")await Ks(k,g.number,{method:u}),await pn("merged",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:k,track:d}),await Zn(g.repo,g.number).catch(()=>{}),await Wo(g.repo,g.number).catch(()=>{}),await Xn(g.repo,g.number).catch(()=>{}),await Pr(g.repo,g.number,"merged").catch(()=>{}),w.action="merged";else{if(y.action==="update-arm"&&(await Ei(k,g.number).catch(()=>{}),w.action="updated-armed"),!await xi(g.repo,g.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await pn("auto-armed",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:k,track:d,...w.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(_){w.ok=!1,w.action="failed",w.reason=_?.message||String(_)}if(Wt(),!p.some(w=>w.ok)){if(o===!0)for(let w of f)await Ao(w.repo,w.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let w of f)await Eo(w.repo,w.number,!1).catch(()=>{});return t.status(502).json({error:p.map(w=>`${w.repo}#${w.number}: ${w.reason}`).join("; "),results:p})}t.json({ok:!0,trackId:d.id,results:p})}catch(u){l=u;let d=u?.message||String(u),f=/invalid|required/i.test(d)?400:502;t.status(f).json({error:d})}finally{i&&c&&await qh(i,c,l?"failed":"completed",l).catch(()=>{})}});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 se(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Gn()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await vi(a.repo,a.number).catch(()=>null),c=await Io(a.repo).catch(()=>null);c&&await Em(c,a.number).catch(()=>{}),i&&(await pn("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 se(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),d,f=Array.isArray(a)?a.filter(Boolean):[];if(f.length)d=f.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(d=await Mo(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of d)try{let g=await Im(w.dir,{title:r,body:o,base:s}),k=w.repo||await _e(w.dir).catch(()=>null);k&&g.number&&await Rt({repo:k,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await pn("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 Im(i.cwd,{title:r,body:o,base:s}),l=await _e(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 pn("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 se(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Mo(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 kw(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 xw(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 qs(n,r),s=await _e(n).catch(()=>null),a=await K$({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(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await Jv(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Ae.join("ci",i.name):"ci",d=Ae.join(a.cwd,".bostrat",u),f=Ae.join(".bostrat",u,`pr-${o.number}-fail.log`);await ht.promises.mkdir(d,{recursive:!0}),await ht.promises.writeFile(Ae.join(a.cwd,f),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
727
|
+
${k.log??"(no fetchable log \u2014 see link)"}
|
|
715
728
|
`).join(`
|
|
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
|
|
729
|
+
`));let p=l.map(k=>k.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 ${f} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Rt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await hv(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await pn("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:f})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await qs(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await _e(n).catch(()=>null),a=await K$({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(d=>d.base===n||s&&d.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 wv(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await pn("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=Mr.filter(d=>e.body.actions.includes(d)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await se(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await H(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await zg(a,c,{actions:o,notify:s});if(d)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await Nt(a.id,Fs(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&&hw(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 se(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Yg(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 Fc=e=>t=>({...t,url:fI(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await ju({trackId:e.query.track||void 0});t.json({views:n.map(Fc(e)),maxLive:Wr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await ST(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Fc(e)(n),session:Xs(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 se(n):null;if(!i&&s&&(i=(await pe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ae.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await bh({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:Fc(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:Fc(e)(await TT(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:Fc(e)(await AT(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 ET(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var jq=[["linear",Go,ng],["jira",Td,eg]],ry=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Qw(),r={};for(let[o,s,a]of jq){let i=await s();if(!i){ry.delete(o),r[o]={configured:!1};continue}let c=ry.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},ry.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 KE(o),i=n?await se(n):null;!i&&r&&(i=(await pe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ae.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 ts=async(e,t)=>{let n=e?await se(e):null;return!n&&t&&(n=(await pe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ae.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await zw()})}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 ts(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 pa(o);await kd(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 ts(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await kd(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var G$=async(e,t,n)=>{if(e)return{ref:e,track:await ts(t,n)};let r=await ts(t,n),o=r?await Jw(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 G$(o,n,r),l=await BE({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 G$(e.query.circuit,e.query.track,e.query.cwd);t.json(await Yw(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 ts(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await FE(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});S.get("/api/decisions",async(e,t)=>{try{let n=await ts(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await UE(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 ts(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Jw(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 jE(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 se(n):null;if(!s&&r&&(s=(await pe()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Ae.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 wa(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 se(r):null;if(!o&&n&&(o=(await pe()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Ae.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 i_(),ticket:await Nn(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 Nn(e.params.id)})});S.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await se(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await wa(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 mg(e.params.id)})});var nf=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 d_(e.params.id))}catch(n){let r=n?.message||String(n);t.status(nf(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await hg(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(nf(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await f_(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(nf(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 se(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await p_(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(nf(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 Ow(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 se(n):null;if(!s&&r&&(s=(await pe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ae.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 h_(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 m_()})}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 se(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await w_(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 qi({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 rr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Uc={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await rr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=an(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Hi(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,Uc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await rr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=an(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Hi(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,Uc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await rr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=an(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(Kt(n,r.v).html,Uc,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&&qu(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let d=a?await se(a):null;if(!d&&c&&(d=(await pe()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+Ae.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=(d.boxes||[]).find(h=>h.name===d.leadBox),p=await HT({trackId:d.id,name:i,html:n?null:s.html,image:r,video:o,cwd:d.cwd,width:l,group:u,box:d.leadBox,agent:f?.agent||null});t.json({ok:!0,mock:p})}catch(d){let f=d?.message||String(d);t.status(/required|invalid|must be/i.test(f)?400:500).json({error:f})}});S.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&qu(n)?n:null;try{let o=await WT(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 GT(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 uA(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await VT(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 sA({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 aA(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 se(n):null;if(!a&&s&&(a=(await pe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ae.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 iA({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 cA(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 lA(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 pe(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Ae.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await yA({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 Ch(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 Ch(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=wA(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 bA(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(ir(n,r.v),Uc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await Ci())});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 pe()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Ae.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Ci();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 Vm({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 se(n):null;if(!s&&r&&(s=(await pe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ae.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 bI(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var Hc=async(e,t)=>{let n=e?await se(e):null;return!n&&t&&(n=(await pe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ae.sep)))||null),n};S.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Hc(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 KS(s,o,{ship:{changedRepos:Mo}})})}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 Hc(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 VS(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 Hc(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:Zm,...await ZS(r)})});var N$=new Set(["reconcile","fixing-ci","rebase","merge"]);S.get("/api/status",async(e,t)=>{let n=await Hc(null,typeof e.query?.cwd=="string"?e.query.cwd:null).catch(()=>null);if(!n)return t.status(400).json({error:"a cwd inside a Track workspace is required"});t.json({ok:!0,trackId:n.id,status:n.status})});S.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await Hc(n,r);if(!c)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let l=o==="fix-ci"?"fixing-ci":o;if(l!=null&&!N$.has(l))throw new Error(`phase must be one of ${[...N$].join(" | ")}`);let u=s==null?null:String(s).trim();if(u&&u.length>120)throw new Error("summary is too long (cap is 120 characters)");let d=Oq();await $(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(f=>f.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});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 se(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await YS(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Nt,unarchiveTrack:cd})})}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 se(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await GS(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 se(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await zS(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})}});rt.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"||se(e.trackId).then(async s=>{if(s&&(n&&await Ni(s,n,o),t==="opened"||t==="pushed")){let a=XS(await H(s).catch(()=>[]));a&&await Ni(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var V$=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await se(n):null;if(!s&&r&&(s=(await pe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ae.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Ci();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=vI(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 V$(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await SI(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 V$(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await TI(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var z$=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 se(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await EI(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(z$(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 se(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await _I(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(z$(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 se(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await kI(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 se(n):null;if(!a&&s&&(a=(await pe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ae.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 kA({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 xA(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 vA(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 SA(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=CI(e.query.since||null);t.json({since:n,repos:Bg(),...await MI(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var Y$=async({trackId:e,cwd:t})=>{let n=e?await se(e):null;return!n&&t&&(n=(await pe()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Ae.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 Y$({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 NI(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 Y$({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 PI(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var Lq=120,oy=400,Aa=null,P$=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>oy)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${oy} cap; a notification is a nudge, put the detail in chat`});let c=Ki(`${n||""}
|
|
730
|
+
${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 se(a):null;if(!l&&s&&(l=(await pe()).find(h=>h.cwd&&(s===h.cwd||s.startsWith(h.cwd+Ae.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),d=(p,h)=>String(p).replace(/\s+/g," ").trim().slice(0,h),f={title:n?d(n,Lq):l.name,body:d(i,oy),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await $(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:f}),t.json({ok:!0,notification:f,delivered:Aa?Aa.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 I$(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 gc(),terms:await ty(),problem:await _$()})});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 E$(n,r)})});S.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await se(e.params.id))return t.status(404).json({error:"track not found"});t.json(await $$())});S.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await se(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 R$())}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 se(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 O$(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 ad({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),d=null;if(c)try{await wa(u,c,{source:"create"})}catch(p){d=p?.message||String(p)}let f=null;if(l)try{await kd(u,await pa(l),{source:"picker"})}catch(p){f=p?.message||String(p)}t.json({ok:!0,track:u,...d?{ticketWarning:d}:{},...f?{circuitWarning:f}:{}})}catch(u){let d=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(d)?400:500).json({error:d})}});S.get("/api/tracks/:id",async(e,t)=>{let n=await se(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:J$(fa(e.params.id)),subtasks:qo(e.params.id).map(Wc),pendingOffers:await uw(n)})});S.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Aw(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await mg(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 Ho(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 Iw(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 cd(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 nd(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 se(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}
|
|
718
731
|
data: ${JSON.stringify(i)}
|
|
719
732
|
|
|
720
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await
|
|
733
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await H(n))i.seq>o&&r(i);let s=Uy(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
721
734
|
|
|
722
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});S.get("/api/tracks/:id/search",async(e,t)=>{let n=await
|
|
723
|
-
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Nt(e.params.id,
|
|
735
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});S.get("/api/tracks/:id/search",async(e,t)=>{let n=await se(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=di(await H(n)),a=Cl(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 ob(),harvest:eb()})});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 sb(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 Fq=["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(`
|
|
736
|
+
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Nt(e.params.id,Fq)})}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 ld(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 se(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 $A(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 se(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=Ae.basename(String(e.params.file||""));if(!r||!Ph(Ae.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=Nh(n.cwd),s,a;try{s=await ht.promises.realpath(o),a=await ht.promises.realpath(Ae.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+Ae.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 Uq=/^[0-9a-fA-F-]{8,64}$/;S.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await se(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(!Uq.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(f=>f.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=tn(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=_q.createInterface({input:ht.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let p;try{p=JSON.parse(f)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let d=l?ep(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",d.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(d.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await sd(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 pw(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 rd(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 Sw({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 fw(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 yw(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 se(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 Bd(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await bw(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 Uw(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Wc(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:qo(e.params.id).map(Wc)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await dw(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:Wc(pd(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:Wc(Hw(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Wc(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 ud(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 ww(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(J$(fa(e.params.id))??{running:!1})});S.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Pw(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=Nw(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 dd(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 jw({...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 J$(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:dl(),paths:kb,trace:await bb({limit:n})})});var sy=Ae.resolve(L$,"..","..","client","dist");ht.existsSync(sy)&&(S.use(jt.static(sy)),S.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(Ae.join(sy,"index.html"),Uc)));var X$=$q.createServer(S),Hq=new Cq({noServer:!0}),Z$=(e,t,n)=>{if(!(Oc(e)||Ta.allowed(Ta.clientIp(e))||iy.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
724
737
|
\r
|
|
725
|
-
`),t.destroy();return}if(
|
|
738
|
+
`),t.destroy();return}if(dI(e,t,n))return;if(!cI(e,cy,{enforce:U$})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
726
739
|
\r
|
|
727
|
-
`),t.destroy();return}let r=
|
|
740
|
+
`),t.destroy();return}let r=Lb(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
728
741
|
\r
|
|
729
742
|
`),t.destroy();return}let o=r.sessionName;if(!Le(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
730
743
|
\r
|
|
731
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
744
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;Hq.handleUpgrade(e,t,n,i=>Db(i,o,{cols:s,rows:a}))};X$.on("upgrade",Z$);var Q$=process.env.BOSTRAT_CERT_DIR||Ae.join(process.env.HOME||ef.tmpdir(),".bostrat","certs"),D$=Number(process.env.BOSTRAT_TLS_PORT)||3443,B$=Ae.join(Q$,"bostrat.globopi.key"),j$=Ae.join(Q$,"bostrat.globopi.crt");if(ht.existsSync(B$)&&ht.existsSync(j$)){let e=Rq.createServer({key:ht.readFileSync(B$),cert:ht.readFileSync(j$)},S);e.on("upgrade",Z$),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(D$,tf,()=>{console.log(`[bostrat-agent] https listening on ${tf||"0.0.0.0"}:${D$}`)})}X$.listen(C$,tf,()=>{console.log(`[bostrat-agent] listening on ${tf||"0.0.0.0"}:${C$}`);let e=xE(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: ${F$}%`),console.log(`[bostrat-agent] ip allowlist: ${Ta.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(iy.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Mw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Lw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),$w().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(()=>Rw()).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)),vw().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)),Jq(),Kq(),Gq(),Yq(),Px({listTracks:pe,getTrack:se}).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)),NA()&&PA({listTracks:pe}),vb().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)),QI(),r$(),g_({resolveTrack:se}),__({listTracks:pe,createTrack:ad,archiveTrack:Ho,listRepos:jn,resolveTrack:se,sayToLead:Nt,answerPrompt:rd,startLeadHandoff:ud,interruptLead:sd,listSubtaskJobs:qo,cancelSubtask:pd,readEvents:H,appendEvent:$,divertShip:zg,answerPrExists:Yg,knownAgents:oc}),Wq(),Ya().then(t=>{t||console.warn(`[boot] no host tmux server on ${It} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),g$(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Aa=OE({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:cy,nodeId:process.env.BOSTRAT_NODE_ID||ef.hostname(),name:process.env.BOSTRAT_NODE_NAME||ef.hostname(),listTracks:pe,detectAgents:td,sampleMetrics:q_,onTracksTransferred:Bw,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||ef.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(()=>{}),P$=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(()=>{}),P$=null},onConnected:()=>{ZI()},onUpdateAvailable:async t=>{try{ay={latest:t.latest,current:t.current},await Cn({...t,policy:kt()}),kt()==="auto"&&await H$({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>Dq(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await Cn({current:t,latest:a,min:n,installKind:o,required:!0,policy:kt(),status:"available",reason:"refused"}),kt()!=="off"&&a){let i=await bd({to:a,kind:Jt,reason:"hard-floor"});i.ok?(Lc.cancel(),await Cn({current:t,latest:a,min:n,installKind:o,required:!0,policy:kt(),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:kt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),fs().catch(()=>{}),EA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),y_().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),JT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),pA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),Tv().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Aa?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function Wq(){qq(),Bu().catch(()=>{}),setInterval(()=>Bu().catch(()=>{}),1e4)}function qq(){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-status","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=Ae.resolve(L$,"..","bin",e),n=Ae.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!ht.existsSync(t))continue;ht.mkdirSync(n,{recursive:!0});let r=Ae.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 Kq(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await pe(),r=await V_({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 Gq(){let e=null,t=async()=>{try{let n=Is(),r=0,o=[],s=null,a=null;try{let u=await pe(),d=await Ag(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await K_(u,{days:7}),a=(await _g(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
745
|
`);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
|
|
746
|
+
`).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 Vq=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,zq=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function Yq(){let e=async()=>{try{let n=await rb();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await pe()).filter(a=>!Ge(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await nb([...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,Vq).unref()},zq).unref()}function Jq(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await pe();for(let n of t)Ge(n)||!n.boxes?.some(r=>r.state==="live")||(await nd(n.id).catch(()=>{}),await Uu(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|