bostrat-node 1.8.2 → 1.8.3
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-followup +3 -0
- package/bin/bostrat-gmail +1 -1
- package/bin/bostrat-ship +2 -0
- package/dist/index.js +254 -215
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var i$=Object.defineProperty;var L=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Pd=(e,t)=>{for(var n in t)i$(e,n,{get:t[n],enumerable:!0})};import{createHash as c$,randomBytes as l$,timingSafeEqual as u$}from"node:crypto";function Pg(){return l$(32).toString("hex")}function xa(e){let t=typeof e=="string"?e.trim():"";return t?c$("sha256").update(t).digest("hex"):null}function Dg(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:u$(Buffer.from(e),Buffer.from(t))}function va(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Dd.set(e,(Dd.get(e)||0)+1),Bd.has(e)&&n-Bd.get(e)<d$)return!1;Bd.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=${Dd.get(e)}${o?` ${o}`:""}`),!0}async function Bg(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 In,$n,Dd,Bd,d$,Yo=L(()=>{In="BOSTRAT_HOOK_TOKEN",$n="x-bostrat-hook";Dd=new Map,Bd=new Map,d$=6e4});import Et from"node:fs/promises";import jg from"node:os";import xt from"node:path";import{execFile as f$}from"node:child_process";import{promisify as p$}from"node:util";function Mc(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function m$(){return process.env.CLAUDE_CONFIG_DIR?xt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):xt.join(jg.homedir(),".claude.json")}function h$(){return process.env.CLAUDE_CONFIG_DIR?xt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):xt.join(jg.homedir(),".claude","settings.json")}async function or(e){if(!e||typeof e!="string")return;let t=xt.resolve(e),n=m$(),r=h$(),o={};try{o=JSON.parse(await Et.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 Et.mkdir(xt.dirname(n),{recursive:!0}),await Et.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Et.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.tui===void 0&&(i.tui="default"),process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' -H "${$n}: $${In}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let f of["SessionStart","UserPromptSubmit","Stop"])i.hooks[f]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await Et.mkdir(xt.dirname(r),{recursive:!0}),await Et.writeFile(r,JSON.stringify(i,null,2)),await w$(t)}async function w$(e){try{let t=xt.resolve(e),n=xt.join(t,".bostrat","memory","claude"),r=xt.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await Et.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(xt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Et.mkdir(n,{recursive:!0}),await Et.mkdir(xt.dirname(r),{recursive:!0}),await Et.writeFile(r,JSON.stringify(o,null,2)),await g$(t,["/.claude/settings.local.json"])}catch{}}async function g$(e,t){try{let{stdout:n}=await p$(f$)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=xt.resolve(e,n.trim()),o=await Et.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await Et.mkdir(xt.dirname(r),{recursive:!0}),await Et.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var Sa=L(()=>{Yo()});import jd from"node:fs/promises";import y$ from"node:os";import Nc from"node:path";function b$(){let e=process.env.CODEX_HOME||Nc.join(y$.homedir(),".codex");return Nc.join(e,"config.toml")}function Lg(e){return`[projects."${e}"]`}function k$(e,t){let n=Lg(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 Jo(e){if(!e||typeof e!="string")return;let t=Nc.resolve(e),n=b$(),r="";try{r=await jd.readFile(n,"utf8")}catch{}if(k$(r,t))return;let o=Lg(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),f=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
|
-
`;r=r.slice(0,a)+
|
|
8
|
+
`;r=r.slice(0,a)+f+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await
|
|
14
|
-
`,"utf8")}var
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await jd.mkdir(Nc.dirname(n),{recursive:!0}),await jd.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Pc=L(()=>{});import Fg from"node:fs/promises";import x$ from"node:os";import Ta from"node:path";function v$(e){let n=Ta.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ta.join(x$.homedir(),".cursor");return Ta.join(r,"projects",n)}async function Xo(e){if(!e||typeof e!="string")return;let t=Ta.resolve(e),n=v$(t),r=Ta.join(n,".workspace-trusted");await Fg.mkdir(n,{recursive:!0}),await Fg.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var Dc=L(()=>{});import Fd from"node:fs/promises";import sr from"node:path";import{execFile as S$}from"node:child_process";import{promisify as T$}from"node:util";async function R$(e){try{return(await Fd.stat(sr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function O$(e){let t=i=>Fd.readFile(sr.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 Rn(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await R$(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:sr.basename(o),branch:await O$(o)});return}if(s>=I$)return;let i;try{i=await Fd.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||$$.has(c.name)||await t(sr.join(o,c.name),s+1)}for(let o of _$)await t(sr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await Se(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 C$(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Se(e){if(!e)return null;if(Ld.has(e))return Ld.get(e);let t=null;try{let{stdout:n}=await E$("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=C$(n)}catch{t=null}return Ld.set(e,t),t}async function Zo(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await Rn())if((await Se(n.path))?.toLowerCase()===t)return n.path;return null}var E$,Ug,A$,_$,I$,$$,Ld,ar=L(()=>{E$=T$(S$),Ug=process.env.HOME||"/home/globo",A$=sr.join(process.env.BOSTRAT_STATE_DIR||sr.join(Ug,".bostrat"),"repos"),_$=[...(process.env.BOSTRAT_REPOS_ROOT||sr.join(Ug,"Documents")).split(","),A$].map(e=>e.trim()).filter(Boolean),I$=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,$$=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Ld=new Map});import M$ from"node:fs";import Hg from"node:fs/promises";import Ea from"node:path";import N$ from"node:os";import{fileURLToPath as P$}from"node:url";import{execFile as D$}from"node:child_process";import{promisify as B$}from"node:util";function _e(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 Bc(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function jc(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!qg.test(n))throw Qo("repo must be owner/name",400);let r=Ud.get(n);if(r&&r.expiresAt-Date.now()>F$)return r;if(!Bc(t))throw Qo("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Kg(`${_e(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Gg(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Qo(`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 Qo(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 Ud.set(n,a),a}async function Aa({env:e=process.env,force:t=!1}={}){if(!Bc(e))return{configured:!1,repos:[],username:null};if(!t&&eo&&Date.now()-eo.at<U$)return eo.data;try{let n=await Kg(`${_e(e)}/v1/github/repos`,{headers:Gg(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return eo={at:Date.now(),data:r},r}catch(n){return eo?eo.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Vg(){Ud.clear(),eo=null}async function sn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Se(e):null);if(!r)return{};let{token:o}=await jc(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Hd({env:e=process.env}={}){return(await Aa({env:e})).username??null}function K$(e){let[t,n]=String(e).split("/");return Ea.join(L$,t,n)}async function Wd(e,{env:t=process.env}={}){let n=(...r)=>Wg("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${W$()}" ${q$(t)}`),await n("config","credential.useHttpPath","true")}async function Lc(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!qg.test(String(e||"")))throw Qo(`bad repo slug "${e}"`,400);let r=K$(e);if(M$.existsSync(Ea.join(r,".git")))return r;let{token:o}=await jc(e,{env:t});await Hg.mkdir(Ea.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Wg("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:H$,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Hg.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Qo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Wd(r,{env:t}),r}var Wg,j$,L$,qg,F$,U$,H$,Kg,Ud,eo,Gg,Qo,W$,q$,it=L(()=>{ar();Wg=B$(D$),j$=process.env.BOSTRAT_STATE_DIR||Ea.join(process.env.HOME||N$.tmpdir(),".bostrat"),L$=Ea.join(j$,"repos"),qg=/^[^/\s]+\/[^/\s]+$/,F$=5*6e4,U$=6e4,H$=10*6e4,Kg=fetch,Ud=new Map,eo=null;Gg=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Qo=(e,t)=>Object.assign(new Error(e),{status:t});W$=()=>P$(new URL("../bin/bostrat-git-credential",import.meta.url)),q$=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Yg(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Xg(e,t,n,r=zg){let o=_e(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await V$(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Zg(e,{env:t=process.env,force:n=!1,timeoutMs:r=zg}={}){if(!Yg(t))return{facts:[],source:"unconfigured"};let o=qd.get(e);if(!n&&o&&Date.now()-o.at<G$)return{facts:o.facts,source:"cache"};try{let s=await Xg(`/v1/memory/${encodeURIComponent(e)}`,{headers:Jg(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 qd.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 Fc(e,t,{env:n=process.env}={}){if(!Yg(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Xg(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Jg(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 qd.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 G$,zg,qd,V$,Jg,Qg=L(()=>{it();G$=6e4,zg=5e3,qd=new Map,V$=(...e)=>fetch(...e);Jg=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Kd from"node:fs/promises";import es from"node:path";import z$ from"node:os";import{EventEmitter as Y$}from"node:events";import{randomUUID as J$}from"node:crypto";function ts(e){return e?.cwd?es.join(e.cwd,".bostrat","events"):es.join(Gd,"events")}async function eR(e,t){if(!Uc.has(t)){let o=(await F(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Uc.set(t,o)}let n=Uc.get(t)+1;return Uc.set(t,n),n}function Vd(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||!X$.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!Z$.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!Q$.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?es.join(Gd,"events"):ts(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let u=e.boxes.find(f=>f.name===t.boxId);o=Vd(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await eR(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||J$(),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 ty(c)&&(c=ny(c),l=JSON.parse(c)),await Kd.mkdir(r,{recursive:!0}),await Kd.appendFile(ey(r,n),c+`
|
|
15
|
+
`),ct.emit("event",l),ct.emit(`track:${n}`,l),l}async function F(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?es.join(Gd,"events"):ts(e));let o;try{o=await Kd.readFile(ey(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(ty(a)?ny(a):a))}catch{}return s}function ry(e,t){let n=`track:${e}`;return ct.on(n,t),()=>ct.off(n,t)}var X$,Z$,Q$,ct,Gd,ey,Uc,tR,ty,ny,de=L(()=>{X$=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","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","loop.create.offered","loop.create.answered","loop.created","loop.update.offered","loop.update.answered","loop.updated","loop.delete.offered","loop.delete.answered","loop.deleted","loop.paused","followup.armed","followup.armed.cancelled","followup.offered","followup.answered","followup.create.offered","followup.create.answered","followup.created","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),Z$=new Set(["user","agent","orchestrator","system","peer"]),Q$=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),ct=new Y$;ct.setMaxListeners(0);Gd=process.env.BOSTRAT_STATE_DIR||es.join(process.env.HOME||z$.tmpdir(),".bostrat");ey=(e,t)=>es.join(e,`${t}.ndjson`),Uc=new Map;tR=/(?<!\\)((?:\\\\)*)\\u0000/g,ty=e=>e.includes("\\u0000"),ny=e=>e.replace(tR,"$1\\ufffd")});import z from"node:fs/promises";import nR from"node:os";import we from"node:path";import{execFile as rR}from"node:child_process";import{promisify as oR}from"node:util";function Wc(){return we.join(qc,"memory","repos")}function to(e){return e===On?we.join(qc,"memory",On):we.join(Wc(),e)}async function Ia(e){try{return JSON.parse(await z.readFile(we.join(e,uy),"utf8"))}catch{return{}}}async function Yd(e,t){let n=await Ia(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await z.mkdir(e,{recursive:!0}),await z.writeFile(we.join(e,uy),JSON.stringify(n))}function sy(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 ay(e){try{let{stdout:t}=await sR("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Jd(e){if(!e)return[];let t=sy(await ay(e));if(t)return[t];let n=[],r=[];try{r=await z.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=sy(await ay(we.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Kc(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 ir(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function an(e,t=null){let n=t??await z.readFile(e,"utf8").catch(()=>""),{fm:r}=Kc(n),o=ir(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await z.stat(e)).mtimeMs}catch{return 0}}function dy(e,t){let n=ir(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===On){let o=ir(e,"trust");if(!o||/^operator/i.test(o))return On}return t}function Xd(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=rs(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=rs(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(f
|
|
24
|
-
`)}await z.mkdir(n,{recursive:!0}),await z.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 fy(e,t){let n=ir(t,"name")||e.replace(/\.md$/,""),r=ir(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function $a(e){let t=[];try{t=await z.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 py(e,t){let n=we.join(e,".bostrat","memory"),r=await $a(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=iy.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let f of r){let d=we.join(n,f);if(o)try{if((await z.stat(d)).mtimeMs<o-iR)continue}catch{continue}let p;try{p=await z.readFile(d,"utf8")}catch{continue}let{fm:h}=Kc(p),w=dy(h,t);if(!w)continue;let g=to(w),k=we.join(g,f),S=await an(d,p);if(l.has(g)||l.set(g,await Ia(g)),(l.get(g)[f]||0)>=S)continue;let A=await an(k).catch(()=>0);if(await z.access(k).then(()=>!0).catch(()=>!1)&&A>=S)continue;await z.mkdir(g,{recursive:!0}),await z.writeFile(k,p),c++;let B=a.get(g);B||a.set(g,B={won:new Set,generated:new Map}),B.won.add(f),B.generated.set(f,fy(f,h)),i.has(w)||i.set(w,new Set),i.get(w).add(f)}let u=await z.readFile(we.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[f,d]of a){let p=we.join(f,"MEMORY.md"),h=await z.readFile(p,"utf8").catch(()=>"");await z.writeFile(p,Xd(h,u,d.won,d.generated))}return iy.set(e,s),{harvested:c,changedByKey:i}}async function my(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await z.access(we.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await Jd(r),a=await py(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 cR(){let e=we.join(qc,"workspaces"),t=[];try{t=await z.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return my(t.filter(n=>n.isDirectory()).map(n=>we.join(e,n.name)))}async function dR(e,t){let n=to(e),r=await Ia(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!lR.test(l)||l==="MEMORY.md")continue;let u=we.join(n,l),f=await z.access(u).then(()=>!0).catch(()=>!1),d=f?await an(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await z.rm(u,{force:!0}),await Yd(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await z.mkdir(n,{recursive:!0}),await z.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=we.join(n,"MEMORY.md"),l=await z.readFile(c,"utf8").catch(()=>""),u=Xd(l,[...s.values()].join(`
|
|
22
|
+
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
23
|
+
`).filter(d=>!f.has(rs(d))).join(`
|
|
24
|
+
`)}await z.mkdir(n,{recursive:!0}),await z.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Gc(e,t=null){let n=to(e),r=t?.length?t:await $a(n);if(!r.length)return{pushed:0,source:"empty"};let o=uR(await z.readFile(we.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=we.join(n,a),c;try{c=await z.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await an(i,c),indexLine:o.get(a)??null})}return Fc(e,s)}async function fR(e,{timeoutMs:t}={}){let{facts:n,source:r}=await Zg(e,t?{timeoutMs:t}:{});n.length?await dR(e,n):r==="plane"&&await Gc(e)}function pR(e){let t=e.split(`
|
|
25
|
+
`);t.length>oy&&(t=t.slice(0,oy));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>aR&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function hR(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=we.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let f=to(u),d=await Ia(f);for(let[g,k]of Object.entries(d)){let S=we.join(n,g);await z.access(S).then(()=>!0).catch(()=>!1)&&await an(S)<=k&&(await z.rm(S,{force:!0}),o++)}let p=await $a(f);if(!p.length)continue;await z.mkdir(n,{recursive:!0});for(let g of p){let k=we.join(f,g),S=we.join(n,g),A=await an(k),R=await an(S).catch(()=>0);await z.access(S).then(()=>!0).catch(()=>!1)&&R>=A||(await z.copyFile(k,S),r++)}let h=await z.readFile(we.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===On?"## 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 z.writeFile(c,u).catch(()=>{});let
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=pR(i);await z.mkdir(n,{recursive:!0}),await z.writeFile(we.join(n,"MEMORY.md"),c);let l=we.join(n,"claude");return await z.mkdir(l,{recursive:!0}),await z.writeFile(we.join(l,"MEMORY.md"),mR,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function hy(e){try{let t=await Jd(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,On];return await Promise.all(n.map(r=>fR(r,{timeoutMs:wR}))),await hR(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function wy(){return{...Xe,pending:ns.size,queued:zd.size}}function gy(e){if(Hc.has(e))return Hc.get(e);zd.add(e);let t=cy.then(async()=>{zd.delete(e),Hc.delete(e);let n=await Qd(e);return Xe.runs++,Xe.lastRunAt=Date.now(),n.error?(Xe.errors++,Xe.lastError=n.error):Xe.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Hc.set(e,t),cy=t.then(()=>{}),t}function Vc(e,{delayMs:t=gR}={}){if(!e||typeof e!="string")return;Xe.scheduled++;let n=ns.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{ns.delete(e),gy(e)},t);r.unref?.(),ns.set(e,r)}async function Zd(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await z.access(we.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=ns.get(e);r&&(clearTimeout(r),ns.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([gy(e),s])}finally{clearTimeout(o)}}async function yy(e){return _a||(_a=(async()=>{try{ly||(ly=!0,await bR());let{harvested:t,changedByKey:n}=await my(e);for(let[r,o]of n)await Gc(r,[...o]);return Xe.reconciles++,Xe.lastReconcileAt=Date.now(),Xe.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return Xe.errors++,Xe.lastError=t?.message||String(t),{harvested:0,error:Xe.lastError}}finally{_a=null}})(),_a)}function yR(){return we.join(qc,"memory",".full-sweep-done")}async function by(){let e=yR();try{let t=JSON.parse(await z.readFile(e,"utf8"));return Xe.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...Xe.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await cR();for(let[o,s]of n)await Gc(o,[...s]);let r={at:Date.now(),harvested:t};return await z.mkdir(we.dirname(e),{recursive:!0}),await z.writeFile(e,JSON.stringify(r)),Xe.backfill=r,{ran:!0,...r}}catch(t){return Xe.errors++,Xe.lastError=t?.message||String(t),{ran:!1,error:Xe.lastError}}}async function bR(){let e=0,t=[];try{t=await z.readdir(Wc(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=we.join(Wc(),n.name),o=to(On),s=new Set,a=new Map;for(let p of await $a(r)){let h=we.join(r,p),w;try{w=await z.readFile(h,"utf8")}catch{continue}let{fm:g}=Kc(w);if(dy(g,n.name)!==On)continue;let k=we.join(o,p),S=await an(k).catch(()=>0),A=await an(h,w);await z.mkdir(o,{recursive:!0}),A>S&&await z.writeFile(k,w),await z.rm(h,{force:!0}),s.add(p),a.set(p,fy(p,g)),e++}if(!s.size)continue;let i=Date.now();await Yd(r,[...s].map(p=>[p,i])),await Fc(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=we.join(r,"MEMORY.md"),l=await z.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(p=>!s.has(rs(p))).join(`
|
|
34
|
+
`);await z.writeFile(c,u).catch(()=>{});let f=we.join(o,"MEMORY.md"),d=await z.readFile(f,"utf8").catch(()=>"");await z.writeFile(f,Xd(d,l,s,a))}return{moved:e}}async function ky(){let e=[],t=[On];try{for(let n of await z.readdir(Wc(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=to(n),o=await $a(r),s=await Ia(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await z.readFile(we.join(r,i),"utf8").catch(()=>""),{fm:l}=Kc(c);a.push({file:i,name:ir(l,"name")||i.replace(/\.md$/,""),description:ir(l,"description")||"",scope:ir(l,"scope")||null,modified:await an(we.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function xy(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=to(e),r=we.join(n,t);if(!await z.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await z.rm(r,{force:!0}),await Yd(n,[[t,s]]);let a=we.join(n,"MEMORY.md"),i=await z.readFile(a,"utf8").catch(()=>"");await z.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>rs(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await Fc(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Qd(e){try{let t=await Jd(e),{harvested:n,changedByKey:r}=await py(e,t[0]||null);for(let[o,s]of r)await Gc(o,[...s]);return n>0&&await xR(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function xR(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,kR)}}await $({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var sR,qc,oy,aR,On,uy,rs,iy,iR,lR,uR,mR,wR,gR,ns,zd,cy,Xe,Hc,ly,_a,kR,Ra=L(()=>{Qg();de();sR=oR(rR),qc=process.env.BOSTRAT_STATE_DIR||we.join(process.env.HOME||nR.tmpdir(),".bostrat"),oy=200,aR=25*1024;On="operator";uy=".tombstones.json";rs=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;iy=new Map,iR=2e3;lR=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,uR=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=rs(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};mR=`# 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
|
+
`;wR=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;gR=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,ns=new Map,zd=new Set,cy=Promise.resolve(),Xe={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Hc=new Map;ly=!1,_a=null;kR=20});import ef from"node:fs/promises";import vR from"node:path";async function vt(e,t,{mode:n}={}){await ef.mkdir(vR.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++SR}.tmp`;await ef.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await ef.rename(r,e)}var SR,cr=L(()=>{SR=0});import Ay from"node:fs/promises";import _y from"node:os";import Iy from"node:path";function Me(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ma(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),TR);try{return await RR(`${_e(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function Ry(){return Yc||(tf||=Ay.readFile($y(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Yc={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>tf=null),tf)}async function Jc(){await vt($y(),Yc).catch(()=>{})}function Ty(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function rf(e){let t=await Ry(),n=Ty(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=>Ty(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>vy&&(t.ops=t.ops.slice(-vy)),await Jc()}function Ey(){zc||(zc=setTimeout(()=>{zc=null,os().catch(()=>{})},OR),zc.unref?.())}function nf(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function MR(e,t=process.env){if(e.op==="record"){let n=await Ma(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Ca(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:$R()}]})},t);if(!n.ok)throw nf(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 Ay.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ma(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Ca(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw nf(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 Ma(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Ca(t)},t);if(!n.ok)throw nf(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function os({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Me(e)?Oa||(Oa=(async()=>{let r=await Ry();NR(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await MR(a,e)}catch(c){if(!c?.refused)return Ey(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<AR)return Ey(),await Jc(),{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>Sy&&(r.parked=r.parked.slice(-Sy)),s++,await Jc();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await Jc()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Oa=null}),Oa):{flushed:0,source:"unconfigured"}}function NR(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=CR);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 ht(e,t,{trackId:n=null,env:r=process.env}={}){return Me(r)?(await rf({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),os({env:r})):{queued:!1,source:"unconfigured"}}async function lr(e,t,n,r,o,{env:s=process.env}={}){return Me(s)?(await rf({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),os({env:s})):{queued:!1,source:"unconfigured"}}async function qt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Me(r)?(await rf({op:"delete",family:e,id:t,deletedAt:n}),os({env:r})):{queued:!1,source:"unconfigured"}}async function nt(e,{env:t=process.env}={}){if(!Me(t))return{records:[],source:"unconfigured"};try{let n=await Ma(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Ca(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 Na(e,t,n,r,{env:o=process.env}={}){if(!Me(o))return null;try{let s=await Ma(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ca(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 TR,ER,vy,AR,Sy,_R,IR,$y,$R,RR,OR,CR,Ca,Yc,tf,zc,Oa,ur=L(()=>{it();cr();TR=1e4,ER=3e4,vy=2e3,AR=5,Sy=100,_R=60*6e4,IR=()=>process.env.BOSTRAT_STATE_DIR||Iy.join(process.env.HOME||_y.tmpdir(),".bostrat"),$y=()=>Iy.join(IR(),"content-outbox.json"),$R=()=>process.env.BOSTRAT_NODE_ID||_y.hostname(),RR=(...e)=>fetch(...e),OR=ER,CR=_R;Ca=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Yc=null,tf=null,zc=null,Oa=null});import of from"node:fs/promises";import Oy from"node:path";async function Cy(e,t,{box:n=null,agent:r=null}={}){if(!e||!Me())return{pushed:!1};try{let o=Date.now();return await ht("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await lr("checkpoints",e,o,"md",t),Pa=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function LR(){if(Pa&&Date.now()-Pa.at<jR)return{records:Pa.records,source:"plane"};let e=await nt("checkpoints");return e.source==="plane"&&(Pa={at:Date.now(),records:e.records}),e}async function Xc(e){try{let t=BR(e);if(!t||!Me())return{restored:!1};let n=Oy.join(e,PR,DR);if(await of.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await LR();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 Na("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await of.mkdir(Oy.dirname(n),{recursive:!0}),await of.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var PR,DR,BR,jR,Pa,Zc=L(()=>{ur();PR=".bostrat/handoff",DR="latest.md",BR=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;jR=6e4,Pa=null});import Da from"node:fs/promises";import FR from"node:os";import Ny from"node:path";function no(e){let t=String(e??"");for(let n of UR)t=t.replace(n,"\u2039redacted\u203A");return t}function Qc(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function HR(e){try{await Da.mkdir(sf,{recursive:!0}),await Da.appendFile(ss,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await Da.readFile(ss,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>My&&await Da.writeFile(ss,`${n.slice(-My).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 ${ss}: ${t?.message||t}`)}}async function re(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&&Qc())try{t.paneTail=no((await Oe(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(Qc()||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 HR(t)}async function Py({limit:e=80}={}){try{return(await Da.readFile(ss,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=no(o.paneTail)),o}catch{return{raw:no(r)}}})}catch{return[]}}var UR,sf,ss,My,Dy,cn=L(()=>{lt();UR=[/[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];sf=process.env.BOSTRAT_DEBUG_DIR||Ny.join(FR.homedir(),".bostrat","debug"),ss=Ny.join(sf,"handoff.jsonl"),My=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;Dy={DEBUG_DIR:sf,LOG_FILE:ss}});import el from"node:fs/promises";import af from"node:path";import WR from"node:os";async function jy(e=By){await el.mkdir(e,{recursive:!0});for(let t of KR){let n=t==="prepare-commit-msg"?GR:t==="post-commit"?VR:`${cf}exit 0
|
|
49
|
+
`,r=af.join(e,t),o=null;try{o=await el.readFile(r,"utf8")}catch{}o!==n&&await el.writeFile(r,n,{mode:493}),await el.chmod(r,493).catch(()=>{})}return e}function Ly({nodeUrl:e,dir:t=By,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",`${Ba}=${e}`]}function Fy(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Ba,qR,By,KR,cf,GR,VR,lf=L(()=>{Yo();Ba="BOSTRAT_NODE_URL",qR=process.env.BOSTRAT_STATE_DIR||af.join(process.env.HOME||WR.tmpdir(),".bostrat"),By=af.join(qR,"git-hooks"),KR=["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"],cf=`#!/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
|
+
`,GR=`${cf}
|
|
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 "$${In}" ] && exit 0
|
|
68
|
+
[ -z "$${Ba}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${$n}: $${In}" "$${Ba}/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
|
+
`,VR=`${cf}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
|
-
[ -z "$${
|
|
79
|
-
[ -z "$${
|
|
78
|
+
[ -z "$${In}" ] && exit 0
|
|
79
|
+
[ -z "$${Ba}" ] && 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 "$${Ba}/api/git/committed" \\
|
|
83
|
+
-H 'content-type: application/json' -H "${$n}: $${In}" --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,d,
|
|
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
|
|
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 Hy}from"node:child_process";import{promisify as zR}from"node:util";import nl from"node:fs";import{randomUUID as Wy}from"node:crypto";function cs(e){return new Promise(t=>setTimeout(t,e))}function tl(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 qy(e){return`/tmp/tmux-${e}/default`}function YR({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=qy(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function La(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${ja}=1`]}function Fa(e,t,n){let r=ro(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&&Ne(n)&&(s+=` --name ${n}`),s}function ro(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function dr(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function ZR(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:ro(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function Ky(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await Gy(e,{loginShell:t});return r===rl?!0:r===as?!1:n}async function Gy(e,{loginShell:t=!1,timeoutMs:n=uf}={}){if(!e)return as;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Ie(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),rl}catch(s){return s?.killed||s?.signal?is:s?.code===1||s?.code===127?as:is}}async function ol(e,{retries:t=QR,sleep:n=eO,probe:r=Gy}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:uf});for(let a=0;s===is&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:uf*2});return s}function G(...e){return["-S",At,...e]}async function fr(){if(!nl.existsSync(At))return!1;try{return await Ie(K,G("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ua(){if(await fr())return!0;try{nl.mkdirSync(At.replace(/\/[^/]+$/,""),{recursive:!0}),await Ie(K,G("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await fr()}function Ne(e){return typeof e=="string"&&tO.test(e)}async function Kt(){if(!nl.existsSync(At))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 Ie(K,G("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,p,h]=r.split("|"),w=Number(i),g=Number(h);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!XR.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ge(e){return(await Kt()).find(n=>n.name===e)||null}function oO(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=rO&&nO.test(t)}function sl(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Vy(e){try{let{stdout:t}=await Ie(K,G("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function iO(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[];for(let o=1;o<=t.length-3;o++)n.push(t.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function cO(e,t){let n=Date.now()+Ha(),r=iO(t),o=!1,s=0;for(;;){if(await cs(sO),s+=1,!await Vy(e))return;let a=await Oe(e,{lines:15}),i=!o&&aO.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Cn(e))return;if(i){o=!0,await re({flow:"spawn",phase:"launch-retry",sessionName:e,agent:dr(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Ie(K,G("send-keys","-t",e,"C-u")),await Ie(K,G("send-keys","-t",e,"-l",t)),await Ie(K,G("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await re({flow:"spawn",phase:"launch-failed",sessionName:e,agent:dr(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function pr(e,t=JR,n={}){if(!oO(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=sl(r);if(!Ne(o))throw new Error("resulting tmux session name is invalid");if(!await Ua())throw new Error(`host tmux server not running on ${At} and auto-start failed \u2014 start one: ${K} -S ${At} new-session -d -s keeper "sleep 2147483647"`);if(await Vy(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||df,a=n.agent||dr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Wy(),l=i?s:Fa(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&ro(s),d=n.firstRun===!0;d||(f&&await or(t),a==="codex"&&await Jo(t),a==="cursor"&&await Xo(t),await hy(t),n.freshWorkspace!==!0&&await Xc(t));let p=f&&!d?Mc():null,h=Pg(),w=["-e",`${In}=${h}`];w.push(...Ly({nodeUrl:Fy()})),p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...La());try{await Ie(K,G("new-session","-d","-s",o,"-c",t,...w))}catch(g){throw tl(p?tl(g,p):g,h)}return await oo(o),await Ie(K,G("set-option","-t",o,"@bostrat-session","1")),await Ie(K,G("set-option","-t",o,"@bostrat-title",r)),await Ie(K,G("set-option","-t",o,"@bostrat-sid",c)),await Ie(K,G("set-option","-t",o,"@bostrat-agent",a)),await Ie(K,G("send-keys","-t",o,l,"Enter")),ZR(l)&&cO(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:h}}async function Gt(e){if(!Ne(e))throw new Error("invalid session name");await Ie(K,G("kill-session","-t",e))}async function zy(e,t){if(Ne(e))try{let n=t?G("set-option","-t",e,"@bostrat-sid",t):G("set-option","-u","-t",e,"@bostrat-sid");await Ie(K,n)}catch{}}async function Oe(e,{lines:t=40}={}){if(!Ne(e))return"";try{let n=G("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Ie(K,n);return String(r||"")}catch{return""}}async function Yy(e){if(!Ne(e))return 0;try{let{stdout:t}=await Ie(K,G("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
88
|
+
`).filter(Boolean).length}catch{return 0}}async function oo(e,{cols:t=dO,rows:n=fO}={}){if(!Ne(e))return!0;if(await Yy(e))return!1;try{let r=await Jy(e);if(r.cols===t&&r.rows===n)return!0;await Ie(K,G("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Ie(K,G("set-window-option","-t",e,"window-size","latest")),await cs(80)}catch{}return!0}async function pO(e){if(!Ne(e))return 0;try{let{stdout:t}=await Ie(K,G("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Cn(e){let t=await pO(e);if(!t)return!1;try{return await Ie("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function Jy(e){if(!Ne(e))return{cols:0,rows:0};try{let{stdout:t}=await Ie(K,G("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 al(e,{cols:t=lO,rows:n=uO}={}){if(Ne(e)&&!await Yy(e))try{let r=await Jy(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Ie(K,G("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Ie(K,G("set-window-option","-t",e,"window-size","latest")),await cs(80))}catch{}}async function Mn(e){return await al(e),Oe(e,{lines:Ze})}async function Be(e,t,{settleMs:n=40,kind:r}={}){if(!Ne(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>mO.has(s));for(let s=0;s<o.length;s++)await Ie(K,G("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await cs(n);return re({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function ls(e,t,{kind:n}={}){if(!Ne(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await Ie(K,G("send-keys","-t",e,"-N",String(r),"BSpace")),re({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function cl(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Ne(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await Ie(K,G("send-keys","-t",e,"-l",s)),n>0&&await cs(n);re({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function hO(e,t){return new Promise((n,r)=>{let o=Hy(K,G(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function wO(e,t){let n=`bostrat-${Wy().slice(0,8)}`;await hO(["load-buffer","-b",n,"-"],t),await Ie(K,G("paste-buffer","-p","-d","-b",n,"-t",e))}async function Qe(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!Ne(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await wO(e,t):await Ie(K,G("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await cs(a),await Ie(K,G("send-keys","-t",e,"Enter"))}re({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 Ie,K,Uy,At,JR,df,ja,ff,XR,rl,as,is,uf,QR,eO,pf,tO,nO,rO,Ha,sO,aO,lO,uO,Ze,dO,fO,mO,il,lt=L(()=>{Sa();Pc();Dc();Ra();Zc();cn();Yo();lf();Ie=zR(Hy);K=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";Uy=typeof process.getuid=="function"?process.getuid():0,At=YR({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return nl.existsSync(qy(Uy))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Uy}),JR=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",df=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",ja="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";ff="bostrat-usage-probe",XR=new Set([ff]);rl="present",as="absent",is="unknown",uf=5e3,QR=[250,1e3,3e3],eO=e=>new Promise(t=>setTimeout(t,e));pf={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"};tO=/^[A-Za-z0-9_.-]{1,64}$/;nO=/^[A-Za-z0-9 ._↻-]+$/,rO=64;Ha=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,sO=1e3,aO=/not found|Unknown command/i;lO=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,uO=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Ze=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,dO=Number(process.env.BOSTRAT_BOX_COLS)||120,fO=Number(process.env.BOSTRAT_BOX_ROWS)||40;mO=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);il=1600});import Wa from"node:fs";import vO from"node:fs/promises";import us from"node:path";import SO from"node:os";function wf(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function gf(e){let t=us.join(hf,wf(e)),n;try{n=Wa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=us.join(t,o);try{let{mtimeMs:a}=Wa.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Vt(e,t){if(!t)return null;let n=us.join(hf,wf(e),`${t}.jsonl`);return Wa.existsSync(n)?n:null}function rb(e){if(!e)return[];let t=us.join(hf,wf(e)),n;try{n=Wa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Wa.statSync(us.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function St(e,t=131072){let n=await vO.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 EO(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 qa(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=Vt(e,t),o=!0,!r)return null}else if(n){if(r=gf(e),o=!1,!r)return null}else return null;let s;try{s=EO(await St(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(nb,s.model),i=nb[s.model]||TO;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 nb,TO,hf,zt=L(()=>{nb={"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},TO=2e5,hf=process.env.BOSTRAT_CLAUDE_PROJECTS||us.join(SO.homedir(),".claude","projects")});function AO(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 Ka(e){let t=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}t.push(n),n="",o=!1}};for(let a=0;a<e.length;a++){let i=e[a];if(r){i===r&&e[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&e[a+1]==="<")return s(),t.push("<<"),t;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),t}function Ga(e){let t=e;for(;t.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[0]);)t=t.slice(1);for(;t.length&&(t[0]==="sudo"||t[0]==="time"||t[0]==="env");)t=t.slice(1);return t}function Re(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(ab(o)){t.has(o)&&r++;continue}n.push(o)}return n}function ib(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 $O(e){let t=ib(e),n=ut(Re(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${je(n)}`:`Read lines ${t.from}\u2013${t.to} of ${je(n)}`:e.includes("-i")&&n?`Edit ${je(n)}`:null}function yf(e){if(e.includes("--files")){let s=ut(Re(e,ob)[0]);return s&&s!=="."?`List files in ${ds(s)}`:"List files"}let t=Re(e,ob),n=t[0];if(!n)return null;let r=ut(t.slice(1).find(s=>s!==".")),o=fs(n,40);return r?`Search for \u201C${o}\u201D in ${ds(r)}`:`Search for \u201C${o}\u201D`}function bf(e){let t=Re(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 kf(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=Re(e)[0];return n?`Run ${je(n)}`:null}function RO(e){let t=Re(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 sb(e){return e.find(t=>/^https?:\/\//.test(t))||null}function MO(e){let t=Ga(Ka(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(je(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return xf(t[2]);let n=je(t[0]);if(CO.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=OO[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function xf(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=AO(t);for(let i=0;i<r.length-1;i++){let c=Ga(Ka(r[i])),l=Ga(Ka(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(je(c[0]))&&je(l[0])==="sed"){let u=ib(l),f=Re(c)[0];if(u&&f)return fs(u.from===u.to?`Read line ${u.from} of ${je(f)}`:`Read lines ${u.from}\u2013${u.to} of ${je(f)}`,88)}}let o=[];for(let i of r){let c=MO(i);c&&o.push({d:c,head:je(Ga(Ka(i))[0]||""),sub:Re(Ga(Ka(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":fs(s?s.d:o[0].d,88)}var fs,ut,je,ds,ab,ob,_O,IO,OO,CO,cb=L(()=>{fs=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,ut=e=>e&&!/[$`]/.test(e)?e:null,je=e=>e.replace(/\/+$/,"").split("/").pop()||e,ds=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},ab=e=>e.startsWith("-"),ob=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);_O={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 ${je(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"},IO={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"};OO={git:e=>{let t=Re(e,new Set(["-C","-c"])),n=_O[t[0]];return n?n(t,e):null},gh:e=>{let t=Re(e),n=IO[t[0]];return n?n(t):null},sed:$O,rg:yf,grep:yf,egrep:yf,nl:e=>{let t=ut(Re(e)[0]);return t?`Read ${je(t)}`:null},cat:e=>{let t=ut(Re(e)[0]);return t?`Read ${je(t)}`:null},head:e=>{let t=ut(Re(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${je(t)}`:null},tail:e=>{let t=ut(Re(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${je(t)}`:null},wc:e=>{let t=ut(Re(e)[0]);return t?`Count lines in ${je(t)}`:"Count input lines"},ls:e=>{let t=ut(Re(e)[0]);return t&&t!=="."?`List files in ${ds(t)}`:"List files"},find:e=>ut(e[1])&&!ab(e[1])&&e[1]!=="."?`Find files in ${ds(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=ut(Re(e)[0]);return t?`Create directory ${ds(t)}`:null},touch:e=>{let t=ut(Re(e)[0]);return t?`Create ${je(t)}`:null},rm:e=>{let t=ut(Re(e)[0]);return t?`Delete ${ds(t)}`:null},mv:e=>{let t=ut(Re(e)[0]);return t?`Move ${je(t)}`:null},cp:e=>{let t=ut(Re(e)[0]);return t?`Copy ${je(t)}`:null},chmod:e=>{let t=ut(Re(e)[1]);return t?`Change permissions on ${je(t)}`:null},diff:e=>{let t=Re(e);return t.length>=2?`Compare ${je(t[0])} and ${je(t[1])}`:null},curl:e=>{let t=sb(e);return t?`Fetch ${fs(t,56)}`:"Fetch a URL"},wget:e=>{let t=sb(e);return t?`Fetch ${fs(t,56)}`:"Fetch a URL"},npm:bf,pnpm:bf,yarn:bf,npx:e=>{let t=Re(e)[0];return t?`Run ${t}`:null},node:e=>kf(e,"Node"),python:e=>kf(e,"Python"),python3:e=>kf(e,"Python"),make:e=>{let t=Re(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:RO,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=Re(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=Re(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Re(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},CO=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function ps(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 mr(e,t=""){let n=t.trim();return n||xf(e)||ps(e)}function vf(e,t){if(e){if(Array.isArray(e)){for(let n of e)vf(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Sf.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))vf(n,t)}}}async function lb(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await St(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(vf(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:mr(f.command,f.description),ts:u})}return o.slice(-n)}function ub(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((h,w)=>w>=s&&(h.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.map(h=>({...h,n:i(h)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:p,ts:Math.max(...p.map(h=>h.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var Sf,ms=L(()=>{zt();cb();Sf=new Set(["Bash"])});import db from"node:fs";import Ef from"node:fs/promises";import Tf from"node:path";import NO from"node:os";async function fb(e){try{return await Ef.realpath(e)}catch{return e}}function DO(e=PO){let t=[],n=r=>{let o;try{o=db.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Tf.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:db.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function BO(e,t=1048576){let n;try{n=await Ef.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 jO(e){let t=await BO(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 so(e,t){if(!e&&!t)return null;let n=DO();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await fb(e);for(let o of n){let s=await jO(o.file);if(s&&(s===e||await fb(s)===r))return o}return null}async function pb(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await so(e,t);if(!o)return[];let s;try{s=await St(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
95
|
+
`)){let p=d.trim();if(!p||p[0]!=="{")continue;let h;try{h=JSON.parse(p)}catch{continue}if((h.type==="turn_context"&&h.payload?.model||h.type==="session_meta"&&h.payload?.model)&&(i=h.payload.model),h.type==="event_msg"){let S=h.payload?.type;S==="task_started"?(l=!0,u=!1,f=h.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&h.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:ao,sourceId:h.payload?.turn_id||f||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i}));continue}if(h.type!=="response_item")continue;let w=h.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,Af.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let g=FO(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 d of a)d.model||(d.model=i);return a.slice(-n)}function UO(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 HO(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 WO(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=HO(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function mb(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await so(e,t);if(!o)return[];let s;try{s=await St(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
97
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let f=[];if(u.type==="function_call"&&Va.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let h=UO(p);h&&(f=[h])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=WO(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,h)=>{a.push({id:d?h?`${d}:${h}`:d:null,tool:u.name,command:p,description:"",summary:mr(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function hb(e,t){let n=await so(e,t);if(!n)return null;try{return Math.round((await Ef.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var PO,LO,FO,Va,Af,ao,io=L(()=>{zt();ms();PO=process.env.BOSTRAT_CODEX_SESSIONS||Tf.join(process.env.CODEX_HOME||Tf.join(NO.homedir(),".codex"),"sessions");LO=new Set(["output_text","text"]),FO=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&LO.has(t.type)).map(t=>t.text).join(`
|
|
98
|
+
`).trim():"";Va=new Set(["exec_command","shell","exec"]),Af=new Set(["wait","write_stdin"]),ao="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 za({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&wb.has(r))return wb.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 gb(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||za({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===ao?"other":za({text:t}),code:null,status:null,requestId:null}}return null}function yb(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:za({text:n}),code:null,status:qO(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function qO(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var x5,wb,ul=L(()=>{io();x5=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),wb=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 KO(e,t){return!e||!t?null:Vt(e,t)}function GO(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 VO(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 zO(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function _f(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 bb(e,t){if(!e.uuid)return null;let n=_f(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 If(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await St(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"&&YO.test(g)&&s.set(w.id,g.split("/").pop())}if(zO(l.content)){let w=bb(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=GO(l.content),d=u==="assistant"?VO(l.content):null,p=u==="user"?bb(c,s):null;if(!f&&!d&&!p)continue;let h=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:za({error:c.error,status:c.apiErrorStatus,text:f})}:null;o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:h||void 0})}return o.slice(-n)}async function kb(e,t,{turns:n=16}={}){let r=KO(e,t);if(!r)return"";let o=await If(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}:** ${JO(s.text||c,700)} ${i}`.trim()}).join(`
|
|
101
|
+
|
|
102
|
+
`):""}var YO,JO,dl=L(()=>{ul();zt();YO=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;JO=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import XO from"node:fs/promises";import{execFile as ZO}from"node:child_process";import{promisify as QO}from"node:util";function Tb(e){Rf=typeof e=="function"?e:null}function Of(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Eb({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||xb.has(r)||(xb.add(r),vb=vb.then(()=>tC({sessionName:e,from:t,to:r})).catch(()=>{}))}async function tC({sessionName:e,from:t,to:n}){await re({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await $f("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await XO.realpath(r),s=Of(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await $f("npm",["install","-g","@openai/codex"],{env:a,timeout:eC});let i=(await $f(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await re({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await Sb({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await re({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await Sb({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function Sb(e){if(Rf)try{await Rf(e)}catch{}}var $f,eC,xb,vb,Rf,fl=L(()=>{cn();$f=QO(ZO),eC=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,xb=new Set,vb=Promise.resolve(),Rf=null});function rC(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
103
|
+
`);if(!Ab.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(nC);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"&&Ab.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 co(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
104
|
+
`);for(let r=n.length-1;r>=0;r--)if($b.test(n[r]))return n.slice(r).join(`
|
|
105
|
+
`);return t}function oC(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 sC(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 aC(e,t=""){let n=co(e);if(Ya.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(_b);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let p=r[d].trim();if(p){if(_b.test(r[d])||/^Press enter/.test(p))break;f=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!$b.test(r[l])&&oC(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=sC(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function lC(e,t=""){let n=co(e);if(Ya.test(n)||!iC.test(n))return null;let r=n.trimEnd().split(`
|
|
107
|
+
`).pop()||"";return cC.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 dC(e,t=""){let n=co(e);if(Ya.test(n)||!Ib.test(n))return null;let r=n.split(`
|
|
108
|
+
`),o=r.findIndex(i=>Ib.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(uC);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 fC(e,t=""){let n=co(e);return Ya.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 pC(e,t=""){let n=co(e);return Ya.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 mC(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Cf.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 wC(e,t=""){let n=String(e);if(!hC.test(n)||Cf.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 gC(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Cf.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 Rb(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=lC(t,n)||dC(t,n)||fC(t,n)||pC(t,n)||aC(t,n);if(r)return r}if(e==="cursor"){let r=wC(t,n)||gC(t,n)||mC(t,n);if(r)return r}return rC(t,{provider:e,box:n})}function Ob(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 nC,Ab,Ya,$b,_b,iC,cC,Ib,uC,Cf,hC,pl=L(()=>{nC=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Ab=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;Ya=/(^|\n)\s*›/,$b=/^\s*Welcome to Codex\b/;_b=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;iC=/Successfully logged in\./,cC=/^\s*Press Enter to continue\.\s*$/;Ib=/Enter this one-time code/,uC=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Cf=/Run Everything|Add a follow-up|Plan, search, build anything/;hC=/click this link to log in|Signing in with the browser/});function xC(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(bC.test(r)||kC.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 AC(e){let t=String(e||"");if(!We.codex.block.test(t))return null;let n=co(t);if(!We.codex.block.test(n))return null;let r=n.match(EC);return{from:r?.[1]||null,to:r?.[2]||null}}function ln(e){return new Promise(t=>setTimeout(t,e))}function Mf(e,t){let n=Date.now(),r=_C();for(let[s,a]of hl)n-a>=r&&hl.delete(s);let o=`${e}::${t}`;return hl.has(o)?!1:(hl.set(o,n),!0)}function Pb(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(Nb.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
113
|
+
`)}function $C(e){let t=0;for(let n of String(e).split(`
|
|
114
|
+
`))Nb.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Nf(e,t){let n=We[e];if(!n?.busy)return{state:"idle",reason:null};if(Df(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(Pb(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=$C(t);return e==="claude"&&r>0&&r<IC?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Pf(e,t){return Nf(e,t).state}function Db(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
115
|
+
`).slice(-OC).join(`
|
|
116
|
+
`);return RC.test(n)}function ws(e,t){return Pf(e,t)==="busy"}function wl(e,t){return Pf(e,t)!=="idle"}function gs(e,t){let n=We[e];return n?.up?n.up.test(t):!0}function CC(e,t){let n=We[e];return n?Df(e,t)?!1:n.up.test(t)&&Pf(e,t)==="idle":!0}function Df(e,t){let n=We[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 Bf(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 Bb(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function Cb(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 Mb(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await oo(e).catch(()=>{});;){let l=Bf(await Oe(e),o);if(CC(t,l)){await s?.(),await ln(hs);return}if(We[t]?.up&&!We[t].up.test(l))if(await Cn(e)){if(c??=Date.now(),Date.now()-c>=Ha()){let u=Cb(t);throw re({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"&&TC.test(l)&&!We.claude.up.test(l)){re({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="codex"&&SC.test(l)&&!We.codex.up.test(l)&&i<3){if(!Mf(e,"codex-trust")){await ln(ml);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await Qe(e,"",{enter:!0,kind:"codex-trust-accept"}),await ln(hs);continue}if(t==="cursor"&&vC.test(l)&&!We.cursor.up.test(l)&&i<3){if(!Mf(e,"cursor-trust")){await ln(ml);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await Qe(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await ln(hs);continue}if(t==="codex"&&i<3){let u=AC(l);if(u){if(!Mf(e,`codex-update:${u.from}->${u.to}`)){await ln(ml);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await Qe(e,"3",{enter:!1,kind:"codex-update-skip"}),Eb({sessionName:e,from:u.from,to:u.to}),await ln(hs);continue}}if(Date.now()>a){if(Df(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=We.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"&&We.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"&&We.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"&&We.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'),re({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await Cn(e)){let u=Cb(t);throw re({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.`),re({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!We[t]?.block?.test(l),paneUp:!!We[t]?.up?.test(l),paneBusy:!!We[t]?.busy?.test(Pb(We[t],l))}).catch(()=>{}),await ln(hs);return}await ln(ml)}}function jf({agent:e,agentCmd:t}={}){let n=t?dr(t):dr(e||"");return{label:n,realClaude:t?ro(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 jb({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=jf({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function Ja({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}={}){let{realCodex:s,realCursor:a}=jf({agent:n,agentCmd:r});return!t||!s&&!a?null:hr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).catch(()=>{})}async function hr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=yC(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:f,realCursor:d}=jf({agent:r,agentCmd:o});if(u&&e&&await or(e),f&&e&&await Jo(e),d&&e&&await Xo(e),(u||f||d)&&n){await Mb(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});return}let p=We[r]?r:We[l]?l:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&p){await Mb(n,p,s,{launchEcho:i});return}await ln(hs)}var yC,ml,hs,bC,kC,We,vC,SC,TC,EC,_C,hl,IC,Nb,RC,OC,Xa=L(()=>{lt();Sa();Pc();Dc();fl();pl();cn();yC=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,ml=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,hs=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,bC=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,kC=/^\s*tab to queue message\b/;We={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:xC},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/}},vC=/Trust this workspace|\[a\]\s*Trust/,SC=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,TC=/Resume from summary/i,EC=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;_C=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,hl=new Map;IC=68,Nb=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;RC=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,OC=15});function DC(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
119
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=PC}function un(e,t){return e==="codex"?{enter:!0,submitDelayMs:MC,...DC(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:NC}:{enter:!0}}var MC,NC,PC,Za=L(()=>{MC=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),NC=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),PC=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function Qa(e,t){if(e==="codex")return UC(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(Lb.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Lb.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(BC);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 UC(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]){(jC.test(t[o])||LC.test(t[o]))&&!Lf.test(t[o])?n=o:Lf.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(Lf);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(FC.test(s))return null;r.unshift(s)}return null}function Ff(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Ue(n.replace(HC,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Ue(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Ue(e){return String(e||"").replace(/\s+/g," ").trim()}function Fb(e,t,{slackMs:n=2e4}={}){let r=Ue(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&&Ue(a.text).includes(r));return s>=0&&s<o.length-1}function Ub(e,t,n,{busy:r=!1}={}){let o=Qa(e,n);return o&&gl(t,o)||o&&e==="codex"&&Ff(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function gl(e,t){let n=Ue(e),r=Ue(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var Lb,BC,Lf,jC,LC,FC,HC,yl=L(()=>{Lb=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,BC=/^❯[ ]?(.*)$/,Lf=/^\s*›[ ]?(.*)$/,jC=/\S+ · \S/,LC=/^\s*(?:\? for shortcuts|tab to queue message)\b/,FC=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;HC=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function ti({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say"},a={}){if(!WC.has(t))return!0;let i=a.waitForUp??jb({agent:t,agentCmd:n}),c=a.capture||(R=>Oe(R,{lines:Ze})),l=a.pressEnter||(R=>Be(R,["Enter"],{kind:"submit-retry"})),u=a.sleep||VC,f=a.now||Date.now,d=a.log||(R=>re(R).catch(()=>{})),p=a.maxMs??GC(),h=f()+p,w={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},g=0,k=0,S=0,A=!1;for(await u(ei[0]);;){let R=await c(e).catch(()=>""),B=gs(t,R),T=ws(t,R),_=Ub(t,r,R,{busy:T});if(_==="submitted")return!0;if(i&&(!B||_==="stuck"&&T)){if(f()>=h)return _==="stuck"?(d({...w,phase:"submit-failed",reason:"stuck-at-deadline",retries:g}),!1):(d({...w,phase:"submit-unverified",reason:A?"cli-down":"cli-never-up",retries:g,waitedMs:p}),!0);await u(qC);continue}if(A=!0,_==="stuck"){if(g>=KC)return d({...w,phase:"submit-failed",retries:g}),!1;g+=1,k=0,S=0,await l(e),d({...w,phase:"submit-retry",attempt:g}),await u(ei[0]);continue}if(k+=1,k>=ei.length||f()>=h)return!0;S+=1,await u(ei[Math.min(S,ei.length-1)])}}var WC,ei,qC,KC,GC,VC,Uf=L(()=>{lt();Xa();yl();cn();WC=new Set(["claude","codex"]),ei=[250,450,700],qC=500,KC=2,GC=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},VC=e=>new Promise(t=>setTimeout(t,e))});function Nn(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of zC){let o=0;for(let s of YC[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Hb(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))JC.has(r)&&(t+=o||0),XC.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function ni(e){return String(e||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var zC,YC,JC,XC,bl=L(()=>{zC=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],YC={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope out\b/,/\btalk through\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/]};JC=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),XC=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Gb(e){let t=ni(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||ZC.has(r)||n.push(r);return n}function QC(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(Kb(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Kb(n,"assistant",o))}}return t}function Kb(e,t,n){let r=Nn(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 ri(e){let t=QC(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Gb(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function kl(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(Gb(t))];if(!i.length)return[];let c=new Map;for(let u of i){let f=e.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(e.N-d+.5)/(d+.5));for(let[h,w]of f){let g=e.lengths[h],k=w+Wb*(1-qb+qb*(g/(e.avgdl||1))),S=p*(w*(Wb+1)/k);c.set(h,(c.get(h)||0)+S)}}let l=[];for(let[u,f]of c){let d=e.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var Wb,qb,ZC,Hf=L(()=>{bl();Wb=1.5,qb=.75,ZC=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 xl from"node:fs/promises";import Vb from"node:path";async function eM(e){let t=Vb.join(e,".bostrat","events"),n;try{n=await xl.readdir(t)}catch{return ri([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await xl.readFile(Vb.join(t,o),"utf8");for(let a of s.split(`
|
|
122
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return ri(r)}function tM(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 nM(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=kl(e,t,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${zb}, 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 Yb(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await xl.readFile(t,"utf8");if(a.includes(zb))return{appended:!1,reason:"already-enriched"};let i=await eM(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=nM(i,tM(a),{k:r,maxChars:o,exclude:s});return c?(await xl.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 zb,Jb=L(()=>{Hf();zb="Relevant prior discussion (verbatim, retrieved"});function cM(e){return rM.test(String(e||"").trimStart())}function Qb(e){let t=String(e||"");if(!cM(t.trimStart()))return null;let n=t.replace(oM,"").trim(),r=lM(n),o=[];for(let u of aM){let f=u.from.map(d=>ys(r.get(d))).find(d=>d&&!Xb(d));f&&o.push({h:u.h,text:oi(f,sM)})}let s=ys(r.get("Current Work"))||ys(r.get("Primary Request and Intent"))||dM(n),a=Sl(s),i=oi(vl(Kf(a[0]||"")),iM)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(oi(vl(Kf(l)),qf));for(let u of["Pending Tasks","Optional Next Step"]){let f=Sl(ys(r.get(u)))[0]||"";if(!(!f||Xb(f))){c.push(oi(`Left to do: ${vl(Kf(f))}`,qf));break}}if(!c.length){let u=Sl(ys(r.get("Problem Solving"))||"")[0];u&&c.push(oi(vl(u),qf))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function lM(e){let t=new Map,n=[];Wf.lastIndex=0;for(let r=Wf.exec(e);r;r=Wf.exec(e))n.push({name:uM(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 uM(e){return Zb.find(n=>n.toLowerCase()===e.toLowerCase())||e}function ys(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 dM(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return ys(t.split(/\n{2,}/).slice(0,2).join(" "))}function Sl(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 Kf(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 vl(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Xb(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function oi(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Sl(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 rM,Zb,Wf,oM,sM,aM,iM,qf,ek=L(()=>{rM=/^This session is being continued from a previous conversation that ran out of context\./,Zb=["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"],Wf=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Zb.join("|")})[:*\t ]*$`,"gim"),oM=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,sM=420,aM=[{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"]}],iM=116,qf=168});function nk(e){return Gf.has(String(e||"").replace(/^\//,"").toLowerCase())}function Tl(e){let t=String(e||"").trim();if(!t)return null;let n=Qb(t);if(n)return n;let r=t.match(fM);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(pM);if(o)return{kind:"skill",name:o[1]};let s=t.match(mM);if(s){let i=s[1].toLowerCase();return Gf.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(TM);if(a&&Gf.has(a[1].toLowerCase()))return{kind:"suppress"};if(hM.test(t))return{kind:"suppress"};if(gM.test(t))return{kind:"suppress"};if(wM.test(t))return{kind:"suppress"};if(yM.test(t)){let i=l=>EM(t.match(l)?.[1].trim()||"")||null,c=i(SM);return{kind:"task",summary:i(bM)||"",taskId:i(kM),toolUseId:i(xM),status:i(vM)?.toLowerCase()||null,resultHead:c?c.length>tk?`${c.slice(0,tk).trimEnd()}\u2026`:c:null}}return null}function EM(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function rk(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 ok(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var fM,pM,mM,hM,wM,gM,yM,bM,kM,xM,vM,SM,tk,Gf,TM,Vf=L(()=>{ek();fM=/^Base directory for this skill:\s*(\S+)/,pM=/^Run the "([^"]+)" workflow\./,mM=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,hM=/^<local-command-(?:stdout|caveat)>/i,wM=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,gM=/^Continue from where you left off\.$/,yM=/^<task-notification>/i,bM=/<summary>([\s\S]*?)<\/summary>/i,kM=/<task-id>([\s\S]*?)<\/task-id>/i,xM=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,vM=/<status>([\s\S]*?)<\/status>/i,SM=/<result>([\s\S]*?)(?:<\/result>|$)/i,tk=600,Gf=new Set(["model","clear","compact","login","exit","quit"]);TM=/^\/([a-z0-9:_-]+)$/i});import AM from"node:crypto";function RM(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,ak)){let o=si(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,_M)){let c=si(i?.label);if(!c)return null;let l=si(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<IM)return null;n.push({question:o,header:si(r?.header).slice(0,$M),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Yf(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;sk.lastIndex=0;let t=null;for(let o;o=sk.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=RM(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Jf(e,t,n){let r=AM.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Xf(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=ak}={}){if(!Array.isArray(e)||!t||!zf.has(n))return null;let s=null;for(let l=e.length-1;l>=0;l--){let u=e[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==t||s.agent!==n)return null;let a=s.ask?.questions||Yf(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Jf(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 ik(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=si(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 zf,ak,_M,IM,$M,sk,si,ck,Zf=L(()=>{zf=new Set(["cursor","codex"]),ak=4,_M=4,IM=2,$M=24,sk=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,si=e=>typeof e=="string"?e.trim():"";ck=`
|
|
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,72 +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 QC(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 tM(e,t,n){let r=Gb(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 dr(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,d=(b,h)=>{u.set(b,h),t.push(h)},f=b=>{let h=u.get(b);h&&(h.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let h=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let g of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let _=g[0].split("/").pop().toLowerCase();m.has(_)||m.set(_,[]),m.get(_).push(b.payload?.ts??b.ts)}}let w=3*6e4,y=20*6e4,x=(b,h)=>!!(b?.name&&m.get(String(b.name).toLowerCase()))?.some(_=>_>=h-w&&_<=h+y);for(let b of e||[])if(b.type==="turn.retracted"){r.add(`${b.boxId}|${b.payload?.saidTs}`);let h=Fe(b.payload?.text);h&&(o.has(b.boxId)||o.set(b.boxId,[]),o.get(b.boxId).push(h))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let h=Fe(b.payload?.text);h&&(s.has(b.boxId)||s.set(b.boxId,new Set),s.get(b.boxId).add(h))}else if(b.type==="job.started"&&b.payload?.id)a.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&c.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let h=b.payload.note,g={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:me(b)};h.toolUseId&&i.set(h.toolUseId,g);let _=String(h.summary||"").match(p);_&&l.set(_[1],g)}let T=new Map,I=new Map,$=null,D=new Map,S=new Map,A=new Map,R=new Map,E=new Map,L=new Map,U=new Map,se=new Map,G=new Map,C=new Map,Z=new Map,Y=new Map,te=new Map,we=new Map,ce=new Map,V=new Map,ge=()=>{for(let b of V.values())b.questions.length&&t.push(b);V.clear()},B=null,ye=6e4,be=new Map,_e=(b,h)=>{let g=o.get(b);if(!g?.length)return!1;let _=Fe(h),ne=s.get(b);return g.some(ae=>{if(!_.startsWith(ae))return!1;let Te=Fe(_.slice(ae.length));return!!Te&&!!ne?.has(Te)})};for(let b of e||[]){let h=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"||!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let g=h.note||gl(h.text);if(g){if(g.kind==="task"){if(g.toolUseId&&a.has(g.toolUseId))break;let _=String(g.summary||"").match(p);if(!g.toolUseId&&_&&c.has(_[1]))break}if(g.kind==="command"&&Lb(g.command))break;if(g.kind==="resume-summary"){t.push({id:b.id,kind:"resume-summary",title:g.title||"Context resumed from summary",bullets:g.bullets||[],sections:g.sections||[],rawChars:g.rawChars??null,ts:me(b),box:b.boxId,agent:b.provider});break}g.kind!=="suppress"&&t.push({id:b.id,kind:"event",type:Ub(g),name:g.name??null,command:g.command??null,summary:g.summary??null,status:g.status??null,ts:me(b),box:b.boxId,agent:b.provider});break}}if(r.has(`${b.boxId}|${me(b)}`)||b.source==="transcript"&&_e(b.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:b.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:me(b),box:b.boxId,agent:b.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(B=null);break}for(let g of D.values())g.status="stale";D.clear();for(let g of A.values())g.status="stale";A.clear(),B&&B.box===b.boxId&&(t.push(QC(B)),B=null),t.push({id:b.id,kind:"message",role:"user",text:h.text,ts:me(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,...b.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let g=h.from||{};t.push({id:b.id,kind:"event",type:"peer-message",text:h.text,from:g.trackName||g.boxId||g.name||null,fromTrackId:g.trackId||null,ts:me(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let g=tb(h,{codexFailedText:Qr});if(g){t.push({id:b.id,kind:"event",type:"provider-error",cls:g.cls,status:g.status??void 0,text:g.text,provider:b.provider,legacy:!0,at:me(b),ts:me(b),box:b.boxId,agent:b.provider});break}ge();let _=(h.images||[]).filter(Ee=>!x(Ee,me(b)));if(!String(h.text||"").trim()&&!_.length)break;if(b.provider==="cursor"){let Ee=`${b.boxId}|${h.text}`;if(n.has(Ee))break;n.add(Ee)}(!B||B.box===b.boxId)&&(B=null);let ne=b.provider==="codex"?(h.tools||[]).filter(Ee=>!wf.has(Ee)&&!Ha.has(Ee)):h.tools||[],ae=h.text,Te=null;if(jf.has(b.provider)){let Ee=Lf(String(h.text||""));Ee&&(ae=Ee.text,Te={questions:Ee.questions})}t.push({id:b.id,kind:"message",role:"assistant",text:ae,ts:me(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,...Te?{ask:Te}:{},...ne.length?{tools:ne}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},..._.length?{images:_}:{}}),Te&&(B={id:b.id,groupId:Ff(b.provider,h.sourceId||b.id,Te.questions),provider:b.provider,box:b.boxId,questions:Te.questions,ts:me(b)});break}case"job.started":{if(!h.id)break;let g=me(b),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let Le=l.get(h.name);Le&&Le.ts>=g&&(_=Le)}let ne={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:_?_.status==="killed"?"stopped":_.status&&_.status!=="completed"?"failed":"done":"running",startedTs:g,finishedTs:_?.ts??null,summary:_?.summary??null,resultHead:_?.resultHead??null},ae=be.get(b.boxId),Te=ae?.jobs[ae.jobs.length-1]?.startedTs;if(ae&&Te!=null&&g-Te<=ye)ae.jobs.push(ne);else{let Le={id:b.id,kind:"job-dock",dockId:h.id,ts:g,box:b.boxId,agent:b.provider,jobs:[ne]};be.set(b.boxId,Le),t.push(Le)}break}case"handoff.done":t.push(Rt(b,"lead-handoff"));break;case"subtask.delegated":t.push({...Rt(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":t.push(Rt(b,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let _ of D.values())_.status="stale";D.clear();for(let _ of A.values())_.status="stale";A.clear()}let g=Rt(b,`pr-${h.change}`);if(g.box=b.boxId??null,eM.has(h.change)&&h.number!=null){let _=tM(T,I,h);if(T.size>=2){for(let ae of I.values())for(let Te of ae)Te.grouped=!0;g.groupPrs=[...T.values()];let ne=[g];for(let ae of T.keys())I.set(ae,ne)}else{let ne=I.get(_)??[];ne.push(g),I.set(_,ne)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=Gb(h),ne=$&&!$.complete||T.size>=2;if(T.has(_))T.delete(_),I.delete(_);else if(h.number!=null)for(let ae of[...T.keys()])ae.endsWith(`#${h.number}`)&&(T.delete(ae),I.delete(ae));if(ne&&h.number!=null){(!$||$.complete)&&($={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:me(b),ts:me(b),box:b.boxId,agent:b.provider},t.push($)),$.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:me(b)}),$.remaining=[...T.values()],$.complete=T.size===0;break}}t.push(g);break}case"pr.offered":{if(!h.offerId)break;for(let _ of D.values())_.status="stale";D.clear();for(let _ of A.values())_.status="stale";A.clear();let g={id:b.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:me(b),box:b.boxId,agent:b.provider};D.set(h.offerId,g),S.set(h.offerId,g),t.push(g);break}case"pr.offer.answered":{let g=S.get(h.offerId);g&&(g.status=h.action||"dismiss",g.actions=Array.isArray(h.actions)?h.actions:null,g.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(g.existingPr={number:h.existingPr.number??null}),D.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let _ of A.values())_.status="stale";A.clear();let g={id:b.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:me(b),box:b.boxId,agent:b.provider};A.set(h.offerId,g),R.set(h.offerId,g),t.push(g);break}case"pr.exists.answered":{let g=R.get(h.offerId);g&&(g.status=h.choice||"dismiss",A.delete(h.offerId));break}case"box.state.changed":{let g=h.change==="model"?"model-change":h.change;if(!g)break;let _=Rt(b,g);if(g==="model-change"&&(_.to=h.model),g==="node-update"&&_.latest){let ne=t.findIndex(ae=>ae.kind==="event"&&ae.type==="node-update"&&ae.latest===_.latest);if(ne>=0){let ae=t[ne];t[ne]={..._,ts:ae.ts,at:ae.at,firstAt:ae.firstAt??ae.ts,updatedAt:_.ts};break}}t.push(_);break}case"track.transferred":case"track.recovered":t.push(Rt(b,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){f(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;d(`view:${h.viewId}`,{id:b.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:me(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{t.push({id:b.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:me(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:b.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:me(b),box:b.boxId,agent:b.provider});break}if(h.change==="deleted"){f(`mock:${h.mockId}`);break}if(h.change!=="created")break;d(`mock:${h.mockId}`,{id:b.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:me(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(h.change==="deleted"){f(`flow:${h.flowId}`);break}if(h.change!=="created")break;d(`flow:${h.flowId}`,{id:b.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:me(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){f(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;d(`runbook:${h.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:me(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{t.push({id:b.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:me(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let _ of U.values())_.box===(h.box||b.boxId)&&(_.status="stale",U.delete(_.offerId));let g={id:b.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:me(b),box:h.box||b.boxId,agent:h.agent||b.provider};U.set(h.offerId,g),se.set(h.offerId,g),t.push(g);break}case"box.fullscreen.answered":{let g=se.get(h.offerId);g&&(g.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",U.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let _ of E.values())_.key===h.key&&(_.status="stale",E.delete(_.offerId));let g={id:b.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:me(b),box:b.boxId,agent:b.provider};E.set(h.offerId,g),L.set(h.offerId,g),t.push(g);break}case"ticket.move.answered":{let g=L.get(h.offerId);g&&(g.status=h.action==="moved"?"moved":"dismiss",E.delete(h.offerId));break}case"ticket.state.changed":{for(let g of E.values())g.key===h.key&&g.to===h.to&&(g.status="stale",E.delete(g.offerId));if(h.mode==="offer")break;t.push(Rt(b,"ticket-state"));break}case"ticket.commented":{t.push({id:b.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:me(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":t.push(Rt(b,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let _ of G.values())_.status="stale";G.clear();let g={id:b.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:me(b),box:b.boxId,agent:b.provider};G.set(h.proposalId,g),C.set(h.proposalId,g),t.push(g);break}case"ticket.create.answered":{let g=C.get(h.proposalId);g&&(g.status=h.action==="created"?"created":"dismiss",g.createdCount=h.createdCount??null,g.failedCount=h.failedCount??null,G.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let _ of Z.values())_.status="stale";Z.clear();let g={id:b.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,nodeId:h.nodeId??null,nodeName:h.nodeName??null,status:"open",ts:me(b),box:b.boxId,agent:b.provider};Z.set(h.proposalId,g),Y.set(h.proposalId,g),t.push(g);break}case"loop.create.answered":{let g=Y.get(h.proposalId);g&&(g.status=h.action==="created"?"created":"dismiss",Z.delete(h.proposalId));break}case"loop.created":{let g=h.proposalId?Y.get(h.proposalId):null;g&&(g.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let ne of te.values())ne.loopId===h.loopId&&(ne.status="stale",ne.staleReason="superseded",te.delete(ne.proposalId));let g=b.type==="loop.update.offered",_={id:b.id,kind:g?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...g?{patch:h.patch??{},changed:Array.isArray(h.changed)?h.changed:Object.keys(h.patch||{})}:{reason:h.reason??""},selfEdit:!!h.selfEdit,status:"open",staleReason:null,ts:me(b),box:b.boxId,agent:b.provider};te.set(h.proposalId,_),we.set(h.proposalId,_),t.push(_);break}case"loop.update.answered":{let g=we.get(h.proposalId);g&&(h.action==="moved"?(g.status="stale",g.staleReason="moved"):g.status=h.action==="applied"?"applied":"dismiss",te.delete(h.proposalId));break}case"loop.delete.answered":{let g=we.get(h.proposalId);g&&(h.action==="moved"?(g.status="stale",g.staleReason="moved"):g.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",te.delete(h.proposalId));break}case"loop.updated":{let g=h.proposalId?we.get(h.proposalId):null;g&&(g.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"loop.deleted":case"loop.paused":break;case"rework.cause.offered":{if(!h.sha||ce.has(h.sha))break;let g={id:b.id,kind:"rework-cause-offer",sha:h.sha,subject:h.subject??"",linesReworked:Number(h.linesReworked)||0,origins:Array.isArray(h.origins)?h.origins:[],heuristic:h.heuristic??null,status:"open",cause:null,ts:me(b),box:b.boxId,agent:b.provider};ce.set(h.sha,g),t.push(g);break}case"rework.cause.answered":{let g=h.sha?ce.get(h.sha):null;g&&(g.status="answered",g.cause=h.cause??null);break}case"ticket.created":{let g={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},_=h.proposalId?C.get(h.proposalId):null;if(_){_.created=g;break}t.push({id:b.id,kind:"ticket-created",...g,ts:me(b),box:b.boxId,agent:b.provider});break}case"error":{if(!h.kind)break;t.push(Rt(b,h.kind));break}case"provider.error":t.push({...Rt(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":t.push(Rt(b,"usage-cap-hit"));break;case"usage.cap.warning":t.push(Rt(b,"usage-cap-warning"));break;case"provider.connected":{t.push({id:b.id,kind:"event",type:"provider-connected",provider:h.provider||b.provider,ts:me(b),at:me(b),box:b.boxId,agent:b.provider});break}case"prompt.answered":{if(B=null,h.control||!h.question)break;let g=h.groupId||h.promptId;if(!g)break;let _=V.get(g);_||(_={id:b.id,kind:"prompt",groupId:g,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:h.questionTotal??1,questions:[],ts:me(b)},V.set(g,_));let ne=h.questionIndex??0,ae={index:ne,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},Te=_.questions.findIndex(Le=>Le.index===ne&&Fe(Le.question)===Fe(ae.question));Te>=0?_.questions[Te]=ae:_.questions.push(ae),_.questions.sort((Le,bt)=>Le.index-bt.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(t.push(_),V.delete(g));break}default:break}}let Ie=u.size?t.filter(b=>!b.retired):t;return rM(Ie.sort((b,h)=>(b.ts??0)-(h.ts??0)))}function rM(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<=nM){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 Wf(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:oM(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??dr(e)).filter(o=>o.kind==="message");return Vy(n,r)}function oM(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==rs(e.command)?t:lr(e.command,e.description||"")}function Rt(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 Vb(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)+sM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&aM.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function zb(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!cM.has(r.type)||!r.box)continue;let o=(r.ts??0)+iM,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 Jb(e){return(e||[]).filter(t=>lM.has(t?.kind)&&t.status==="open")}var me,eM,Gb,nM,sM,aM,iM,cM,lM,qf=j(()=>{os();eo();rl();dl();Bf();Hf();me=e=>e.payload?.ts??e.ts;eM=new Set(["opened","ci-fixed","reconciled","pushed"]),Gb=e=>`${e.repo??""}#${e.number}`;nM=2*6e4;sM=12e4,aM=new Set(["archived","unarchived","opened"]);iM=2500,cM=new Set(["pr-opened","pr-merged","pr-auto-armed"]);lM=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","fullscreen-offer"])});function mM(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 wM(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 gM(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<fM&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<pM&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Qb(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=mM(e),s=wM(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=dr(e??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-uM);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Zb[r]??Zb.failover),l.push(""),o.lastPush?.sha){let f=Xb(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=Xb(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=gM(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"}:** ${hM(f.text,dM)}`),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
|
|
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 _M(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(rk.test(s)){if(r.length>=mk)continue;let a=EM(s.replace(rk,"").replace(/[.;,:]+$/,""),hk,TM);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>nk&&(o=`${o.slice(0,nk-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function wk(e,t,{timeoutMs:n=9e4}={}){let r=pr.join(t,vM),o=pr.join(r,SM);await fr.mkdir(r,{recursive:!0});let s=await yl(o),a=await fe(e);if(!a)throw new Error(`session "${e}" not found`);return await bl(e,a,AM(o)),await Sk(o,s,n),_M(await fr.readFile(o,"utf8"))}function gk(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===uk||n===pk;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(lk)||n===bM||r.startsWith(dk)||r.startsWith(fk)}function yk(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-IM;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 bk(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 kk(e,{windows:t=[]}={}){if(gk(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 kl(e){return Kf.get(e)||null}async function xk(e){let t=Kf.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await fe(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:pr.join(n.cwd,ak,ik)};return Kf.set(e,r),OM(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function OM(e,t){e.step="summarize",await vk(e.name,t),e.step="clear";let n=await fe(e.name);await bl(e.name,n,"/clear"),await zf($M),e.step="resume",await bl(e.name,n,xM),e.step="done",e.finishedAt=Date.now()}async function vk(e,t,{timeoutMs:n=RM}={}){let r=pr.join(t,ak),o=pr.join(r,ik);await fr.mkdir(r,{recursive:!0});let s=await yl(o),a=await fe(e);await ee({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await bl(e,a,kM(o)),await Sk(o,s,n),await ee({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await yl(o)});let i=await Cb(t,o,{exclude:u=>gk(u.role,u.text)});await ee({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await O({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 fr.readFile(o,"utf8");await O({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...ck(u),ts:Date.now()}})}catch{}}await Wd(t);let l=await CM(r,o);return c&&await hy(c,l||o,{box:e,agent:a?.agent??null}),o}async function Gf(e,{timeoutMs:t}={}){let n=await fe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return vk(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Vf(e,t,{trackName:n="this Track"}={}){let r=await F({id:e,cwd:t}),o=Qb(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=pr.join(t,ok);return await fr.mkdir(pr.dirname(s),{recursive:!0}),await fr.writeFile(s,o,"utf8"),await O({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...ck(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),ok}async function yl(e){try{return(await fr.stat(e)).mtimeMs}catch{return 0}}async function Sk(e,t,n){let r=Date.now()+n;for(;;){if(await yl(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await zf(2e3)}}async function CM(e,t){try{if(!yM.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=pr.join(e,`${n}.md`);return await fr.copyFile(t,r),r}catch{return null}}function zf(e){return new Promise(t=>setTimeout(t,e))}function sk(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 bl(e,t,n){if(t){let o=sk(t.agent);await ur({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await fe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await De(e,["Escape"],{kind:"drive-wipe"}),await zf(150),await Qo(e,el,{kind:"drive-wipe"}),await Je(e,n,{kind:"orchestration-drive",...sn(r.agent,n)}),await Ya({sessionName:e,agent:r.agent,agentCmd:sk(r.agent),text:n,flow:"orchestration-drive"})}var ak,ik,tk,lk,uk,dk,bM,kM,xM,fk,pk,vM,SM,nk,mk,hk,TM,AM,rk,EM,IM,$M,RM,Kf,ok,Jf=j(()=>{nt();Ga();rn();Va();Of();Mb();Aa();ue();Kc();ek();ak=".bostrat/handoff",ik="latest.md",tk=32*1024;lk="Checkpoint: write a work-in-progress handoff",uk="HANDOFF WRITTEN",dk="Resume from .bostrat/handoff/latest.md",bM="/clear",kM=e=>`${lk} 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 ${uk} and take no further action.`,xM=`${dk}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,fk="Beat: write a",pk="BEAT WRITTEN",vM=".bostrat/arc",SM="beat.txt",nk=200,mk=4,hk=5,TM=36,AM=e=>`${fk} 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 ${mk} further lines, each starting with "- " and naming ONE piece in at most ${hk} 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 ${pk} and take no further action.`,rk=/^(?:[-–—*•·]|\d+[.)])\s+/,EM=(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};IM=6e5;$M=3500,RM=3e5,Kf=new Map;ok=".bostrat/handoff/status-latest.md"});function hr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!xl.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 Ek(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:hr(o.from,"from",n),toMs:hr(o.to,"to",n)}))}return t}function Xf(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 NM(e){if(!e)return null;let t=String(e).trim();return mr[t]?t:Tk[t]?Tk[t]:Object.keys(mr).find(r=>t.startsWith(r))||null}function PM(e){if(!e||e===mr)return MM;let t=Ak.get(e);return t||(t=Ek(e),Ak.set(e,t)),t}function _k(e,t,n={}){let{table:r,speed:o}=n,s=PM(r),a=r&&r!==mr?s[e]?e:null:NM(e);if(!a)return null;let i=Xf(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 an(e,t={},n,r){let o=_k(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 DM(e=mr){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:hr(a.from,"from",n),toMs:hr(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`),xl.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 jM({now:e=Date.now(),table:t=mr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...BM,...n},a=[],i=Xf(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:hr(d.from,"from",c),toMs:hr(d.to,"to",c),verifiedMs:xl.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*Yf;f&&i-d.verifiedMs>r*Yf&&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*Yf&&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 ls({now:e=Date.now(),table:t=mr,...n}={}){let r=Xf(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=hr(l.from,"from",i),d=hr(l.to,"to",i);r>=u&&r<=d&&xl.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=[...DM(t),...jM({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var mr,Tk,xl,MM,Ak,Ik,BM,Yf,On=j(()=>{mr={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Tk={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"},xl=/^\d{4}-\d{2}-\d{2}$/;MM=Ek(mr);Ak=new WeakMap;Ik=(e,t,n)=>_k(e,t,n)!==null;BM=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Yf=864e5});function LM(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Rk(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Ot(e){let t=e.payload?.model||"unknown",n=LM(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=an(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&&!Ik(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function wr(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=$k();for(let f of n){let{usd:p,model:m,tokens:w,repriced:y,unpriced:x,asRecorded:T}=Ot(f);i+=T;let I=f.provider||"unknown",$=f.boxId||"unattributed";if(y&&c++,x&&(l++,u.add(m)),a+=p,Rk(d,w),r[m]||={usd:0,tokens:$k(),events:0},r[m].usd+=p,r[m].events+=1,Rk(r[m].tokens,w),o[I]||={usd:0,events:0},o[I].usd+=p,o[I].events+=1,s[$]||={usd:0,events:0,provider:I,models:new Set,label:t[$]||$},s[$].usd+=p,s[$].events+=1,m!=="unknown"&&s[$].models.add(m),f.epoch!=null){let D=(s[$].epochs||={})[f.epoch]||={usd:0,events:0,provider:I,models:new Set};D.usd+=p,D.events+=1,D.provider=I,m!=="unknown"&&D.models.add(m)}}return{totalUsd:us(a),asRecordedUsd:us(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:vl(r,f=>({...f,usd:us(f.usd)})),byProvider:vl(o,f=>({...f,usd:us(f.usd)})),byBox:vl(s,f=>({usd:us(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:vl(f.epochs,p=>({usd:us(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var $k,us,vl,gr=j(()=>{On();$k=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});us=e=>Math.round(e*1e4)/1e4,vl=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function FM(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 Ok(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(Zf(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Zf(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(`${Zf(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=wr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?FM(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Zf,Ck=j(()=>{gr();Zf=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as UM}from"node:child_process";import{promisify as HM}from"node:util";import{randomUUID as WM}from"node:crypto";async function Qf(e,t){let n=Date.now()+t;for(;;){if(await In(e))return!0;if(Date.now()>n)return!1;await ti(KM)}}async function VM(e,t){let n=Nk[t]||Nk.default;n.escape&&(await De(e,["Escape"],{kind:"swap-exit"}),await ti(150)),n.wipe&&await Qo(e,el,{kind:"swap-exit"}),await tl(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=sn(t).submitDelayMs||0;await ti(Math.max(r,250)),await De(e,["Enter"],{kind:"swap-exit"})}async function Pk(e,t){try{let{stdout:n}=await no(W,q("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Dk(e){await no(W,q("respawn-pane","-k","-t",e))}async function ep(e,t={}){if(!Ne(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await fe(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||ar(n),a=Number(t.exitTimeoutMs)||qM;await ee({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await In(e)&&(await VM(e,o),!await Qf(e,a)&&(i=!0,await ee({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Dk(e),!await Qf(e,Mk))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Yr(n),l=c?Ec():null,u=await Pk(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await no(W,q("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await no(W,q("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(y){throw l?zc(y,l):y}if(d&&!i&&(await Dk(e),!await Qf(e,Mk)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,p=t.resumeSessionId||WM(),m=f?n:Pa(n,p,e),w=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Qn(r.cwd),s==="codex"&&await Fo(r.cwd),s==="cursor"&&await Uo(r.cwd),await Xr(e),await no(W,q("set-option","-t",e,"@bostrat-sid",p)),await no(W,q("set-option","-t",e,"@bostrat-agent",s)),await ti(GM),c&&Na().length&&(await Pk(e,Ma)||await no(W,q("set-environment","-t",e,Ma,"1")).catch(()=>{}),await Je(e,`export ${Ma}=1`,{kind:"swap-env"}),await ti(150)),await Je(e,m,{kind:"swap-launch"}),await ee({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:d,launchCmd:m}}var no,ti,qM,Mk,KM,GM,Nk,Bk=j(()=>{nt();ya();Ic();$c();Va();rn();no=HM(UM),ti=e=>new Promise(t=>setTimeout(t,e)),qM=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Mk=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,KM=250,GM=400,Nk={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 jk(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await gt(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 H0(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&&!L0.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=F0.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!VN.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ii(e,t,{timeoutMs:n=KN}={}){let r=Date.now()+n;for(;;){let o=await Oe(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Ml(GN)}}async function YN(e,t){return await Je(e,`/model ${t.model}`,sn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function XN(e,t){await Qc(e),await Je(e,"/model",{enter:!1}),await Ml(250),await De(e,["Enter"],{kind:"model-switch"});let n=await ii(e,P0);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await De(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=JN(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Je(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await ii(e,i=>D0(i)||!P0(i)&&o(i)&&{done:o(i)});if(!s)throw await De(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?L0.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 De(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Je(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ii(e,i=>!D0(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 ZN(){let e=N0.join(process.env.CURSOR_CONFIG_DIR||N0.join(qN.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await WN.readFile(e,"utf8"))?.model||null}catch{return null}}async function QN(e,t){await Qc(e),await tl(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await ii(e,B0),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 ii(e,B0,{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 De(e,["Down"],{kind:"model-switch"}),await Ml(250)}if(!n||!s||!o(s)){await De(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await De(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 De(e,["Enter"],{kind:"model-switch"}),await Ml(800);let a=await ZN();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 W0(e,t,n){return e==="codex"?XN(t,n):e==="cursor"?QN(t,n):YN(t,n)}var Ml,KN,GN,j0,VN,zN,L0,F0,q0=j(()=>{nt();Va();Ml=e=>new Promise(t=>setTimeout(t,e)),KN=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,GN=300,j0=[{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"}],VN=new Set([...j0.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"]),zN=[{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"}],L0=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),F0=[{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 Pl(e){let t=e||{},n=eP(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function eP(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:lt(e.agent),payload:{...kr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:lt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...kr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...kr(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",...kr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...kr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...kr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(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:lt(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:lt(e.agent),payload:{...kr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...kr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function V0(e,t){let n=Pl(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Cp(()=>O(r,n))}async function z0(e){if(!(!e?.id||K0.has(e.id))){K0.add(e.id);try{let t=await F(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=Pl(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await O(e,o)}}catch{}}}function kr(e){let{type:t,at:n,...r}=e;return r}function J0(e,t,n){!n?.promptId||Op.get(e.id)===n.promptId||(Op.set(e.id,n.promptId),Cp(()=>O(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:lt(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function Mp(e,t,n,r={}){Op.delete(e.id),Cp(()=>O(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,Nl,Cp,G0,K0,Op,Np=j(()=>{ue();lt=e=>e||null;Nl=Promise.resolve(),Cp=e=>(Nl=Nl.then(e).catch(()=>{}),Nl),G0=()=>Nl;K0=new Set;Op=new Map});function sP(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function tx(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of sP(n).reverse()){if(!tP.test(r))continue;let o=r.endsWith("?")&&r.length<=oP&&rP.test(r);if(nP.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function rx(e){let t=String(e||""),n=[];aP.test(t)&&n.push("commit"),nx.test(t)&&n.push("rebase"),iP.test(t)&&n.push("push");let r=lP.test(t);!r&&cP.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 ox(e){return nx.test(String(e||""))}function vs(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let f=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(f+=` ${d.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(f+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),f}function sx(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 ax(e,t=Date.now()){let n=[],r=null;for(let o of e||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&t-r.lastActivity<=uP&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function ix(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+dP||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function cx(e){let t=(Array.isArray(e)?e:[]).filter(r=>Y0[r]).map(r=>Y0[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 X0,Z0,Q0,ex,tP,nP,rP,oP,xs,aP,nx,iP,cP,lP,uP,dP,Y0,Ss=j(()=>{X0=/(?<!\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,Z0=/(?<!\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,Q0=/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,ex=/(?<!\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,tP=new RegExp(`\\b(?:${X0}|${Z0}|${Q0}|${ex})\\b`,"i"),nP=/\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,rP=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,oP=90;xs=["commit","rebase","push","pr"],aP=new RegExp(`\\b(?:${X0})\\b`,"i"),nx=new RegExp(`\\b(?:${ex})\\b`,"i"),iP=new RegExp(`\\b(?:${Z0})\\b`,"i"),cP=new RegExp(`\\b(?:${Q0})\\b`,"i"),lP=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;uP=3e4;dP=2500;Y0={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import fP from"node:crypto";import Dl from"node:fs/promises";import lx from"node:path";import pP from"node:os";async function Ue(){return xr||(Pp??=Dl.readFile(Bp,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(xr=e,!t&&Me()){let{records:n,source:r}=await Ye("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;xr[re(i.repo,i.number)]=i}Object.keys(xr).length&&await jp().catch(()=>{})}}return xr}).finally(()=>Pp=null),Pp)}async function jp(){let e=JSON.stringify({links:xr},null,2),t=`${Bp}.${process.pid}.tmp`;await Dl.mkdir(ux,{recursive:!0}),await Dl.writeFile(t,e),await Dl.rename(t,Bp)}async function Xe(e){Dp=Dp.then(jp,jp),await Dp.catch(()=>{});let t=xr?.[e];t&&ct("prlinks",{id:dx(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function St({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=re(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:mP.has(a),firstSeen:Date.now()},await Xe(re(e,t)),{link:i[c],created:!0})}async function Nn(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||n.announced?null:(n.announced=!0,await Xe(re(e,t)),n)}async function Qe(e,t){return!e||t==null?null:(await Ue())[re(e,t)]??null}async function vr(e,t,n){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await Xe(re(e,t)),r):null}async function fx(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[re(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(re(e,t)),o):null}async function px(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[re(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(re(e,t)),o):null}async function Bl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Xe(re(e,t)),r)}async function jl(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Xe(re(e,t)),n)}async function Ll(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Xe(re(e,t)),r)}async function Fl(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Xe(re(e,t)),n)}async function mx(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(r.baseFoldAt=n,await Xe(re(e,t)),r):null}async function hx(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Xe(re(e,t)),n)}async function wx(e,t,n){if(!e||t==null||!n)return null;let r=(await Ue())[re(e,t)];return r?(r.lastHeadSha=n,await Xe(re(e,t)),r):null}async function gx(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Ue())[re(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 Xe(re(e,t))),{prev:o,link:r}}async function ci(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Xe(re(e,t)),r):null}async function fo(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Xe(re(e,t)),r):null}async function li(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Xe(re(e,t)),r):null}async function yx(e,t,n){if(!e||t==null)return null;let r=(await Ue())[re(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 Xe(re(e,t)),r}async function ui(e,t){if(!e||t==null)return null;let n=(await Ue())[re(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 Xe(re(e,t)),n)}async function bx(){return Object.values(await Ue()).filter(e=>!hP.has(e.state))}async function po(){return Object.values(await Ue()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function kx(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("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 ct("prlinks",{id:dx(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var ux,Bp,re,dx,xr,Pp,Dp,mP,hP,Pn=j(()=>{sr();ux=process.env.BOSTRAT_STATE_DIR||lx.join(process.env.HOME||pP.tmpdir(),".bostrat"),Bp=lx.join(ux,"pr-links.json"),re=(e,t)=>`${e}#${Number(t)}`,dx=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=fP.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},xr=null,Pp=null,Dp=Promise.resolve();mP=new Set(["api","observed","reconciled"]);hP=new Set(["merged","closed","lost"])});import wP from"node:fs";import Lp from"node:fs/promises";import Tx from"node:path";import gP from"node:os";function _x(e,t=!1){if(!t&&e-vx<yP)return;vx=e;let n=JSON.stringify({windowStart:Ts,byCaller:Object.fromEntries(Dn),lastHour:Sr});Sx=Sx.then(()=>Lp.mkdir(Ax,{recursive:!0})).then(()=>Lp.writeFile(`${Ul}.tmp`,n)).then(()=>Lp.rename(`${Ul}.tmp`,Ul)).catch(()=>{})}function Ix(e){e-Ts<Ex||(Sr={start:Ts,total:Hl(Dn),byCaller:Object.fromEntries(Dn)},Sr.total&&console.log(`[github] ${Sr.total} calls last hour`,Sr.byCaller),Ts=e,Dn=new Map,_x(e,!0))}function Bn(e="other",t=Date.now()){Ix(t),Dn.set(e,(Dn.get(e)||0)+1),_x(t)}function $x(e=Date.now()){return Ix(e),{windowStart:Ts,total:Hl(Dn),byCaller:Object.fromEntries(Dn),lastHour:Sr,backoffUntil:mo>e?mo:null}}function Tr(e){return bP.test(String(e?.message||e||""))}function Ar(e,t=Date.now()){return mo<=t&&console.warn(`[github] rate limited \u2014 backing off ${xx/6e4} min:`,String(e?.message||e||"").slice(0,200)),mo=Math.max(mo,t+xx),mo}function Wl(e=Date.now()){return Math.max(0,mo-e)}var Ax,Ul,Ex,xx,yP,Ts,Dn,Sr,mo,vx,Hl,Sx,bP,di=j(()=>{Ax=process.env.BOSTRAT_STATE_DIR||Tx.join(process.env.HOME||gP.tmpdir(),".bostrat"),Ul=Tx.join(Ax,"github-metrics.json"),Ex=60*6e4,xx=5*6e4,yP=6e4,Ts=Date.now(),Dn=new Map,Sr=null,mo=0,vx=0,Hl=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(wP.readFileSync(Ul,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Ex?(Ts=e.windowStart,Dn=new Map(Object.entries(e.byCaller||{})),Sr=e.lastHour||null):Hl(new Map(Object.entries(e.byCaller||{})))&&(Sr={start:e.windowStart,total:Hl(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Sx=Promise.resolve();bP=/rate limit|too quickly|abuse detection|HTTP 429/i});import Rx from"node:fs/promises";import Ox from"node:path";import kP from"node:os";function Kl({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};ql.push(l),ql.length>vP&&ql.shift();let u=Fp.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)),Fp.set(e,u),SP(l),l}async function SP(e){try{await Rx.mkdir(Cx,{recursive:!0}),await Rx.appendFile(xP,JSON.stringify(e)+`
|
|
179
|
-
`)}catch{}}function Mx(){let e={};for(let[t,n]of Fp)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:ql.slice(-20)}}var Cx,xP,vP,ql,Fp,Gl=j(()=>{Cx=process.env.BOSTRAT_STATE_DIR||Ox.join(process.env.HOME||kP.tmpdir(),".bostrat"),xP=Ox.join(Cx,"github-latency.ndjson"),vP=100,ql=[],Fp=new Map});import{execFile as TP}from"node:child_process";import{promisify as AP}from"node:util";async function EP(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Es("git",["-C",e,"rev-parse","HEAD"]),Es("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await O({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function Ct(e,t,n={}){let{caller:r="prs",...o}=n,s=await tn({cwd:e});Bn(r);try{let{stdout:a}=await Es("gh",t,{cwd:e,..._P,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Tr(a)&&Ar(a),a}}async function Wp(){if(Vl&&Date.now()-Vl.at<6e4)return Vl.data;let e;try{Bn("auth");let t=await Es("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 xa();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Vl={at:Date.now(),data:e},e}async function IP(e){try{let{stdout:t}=await Es("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Px(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 Up(e){let t=(e||[]).map(Px),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 Kp(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function Bx(e,t){if(!t)return null;let n=await xe(e);if(!n)return null;try{let r=await Ct(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 jx(e){return(e||[]).map(t=>({name:t.name,detailsUrl:t.html_url||null,status:String(t.status||"").toUpperCase(),conclusion:t.conclusion?String(t.conclusion).toUpperCase():null,completedAt:t.status==="completed"&&t.updated_at||null}))}async function Lx(e,t){let n=await Bx(e,t);return n===null?{...Up([]),unavailable:!0}:Up(jx(n))}function zl(e,t=Up(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function RP(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){As.delete(s);continue}r.add(s),As.has(s)||As.set(s,n),o.mergeabilityPending=n-As.get(s)<$P}for(let o of As.keys())o.startsWith(`${e}#`)&&!r.has(o)&&As.delete(o);return t}async function _s(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 Ct(n.path,["pr","view",String(r),"--json",qp]);return zl(JSON.parse(o))}catch(o){if(!Kp(o))throw o;let s=await Ct(n.path,["pr","view",String(r),"--json",Dx]),a=JSON.parse(s);return zl(a,await Lx(n.path,a.headRefOid))}}async function cn(e,{caller:t="prs"}={}){try{let n=await Ct(e,["pr","list","--json",qp,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>zl(r)).sort(Nx)}catch(n){if(!Kp(n))throw n;let r=await Ct(e,["pr","list","--json",Dx,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>zl(o,await Lx(e,o.headRefOid))))).sort(Nx)}}function un(){fi.clear()}async function MP(e,t){if(!e)return t;for(let n of t){let r=await Qe(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Bl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ll(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function Fx(){let e=await Wp();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await En())await IP(a.path)&&t.push(a);let n=Date.now(),r=Wl(n)>0,o=await Promise.all(t.map(async a=>{let i=fi.get(a.path),c=i?.pending?CP:OP;if(i&&(r||n-i.at<c))return i.entry;try{let l=await xe(a.path).catch(()=>null),u=RP(a.path,await cn(a.path,{caller:"tab"}),n);if(await MP(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&&Kl({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 fi.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:ln(l)}}})),s=new Set(t.map(a=>a.path));for(let a of fi.keys())s.has(a)||fi.delete(a);return{auth:e,repos:o}}async function jn(e){let t=(await En()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ho(e){return Ho(e)}async function Is(e,t,{method:n="squash"}={}){let r=await jn(e);if(!pi.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 tn({cwd:r.path})).GH_TOKEN){let a=await Cd().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ct(r.path,s)}catch(a){throw new Error(ln(a))}finally{un()}return{ok:!0,number:o}}async function mi(e,t){let n=await jn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ct(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(ln(s))}finally{un()}let o=await xe(n.path).catch(()=>null);return o&&await mx(o,r).catch(()=>{}),{ok:!0,number:r}}async function Gp(e,t){let n=await jn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ct(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ln(o))}finally{un()}return{ok:!0,number:r}}function Vp(e){return e.green&&!e.isDraft}function BP(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 Ux(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Vp(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Hx(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function hi(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 Wx(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(hi))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function qx(){let e={};for(let[t,n]of Hp)e[t]={...n};return e}async function Kx(e,{method:t="merge",onMerged:n=null}={}){let r=await jn(e);if(!pi.has(t))throw new Error(`invalid merge method: ${t}`);if(Hp.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await cn(r.path)).filter(Vp).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 Hp.set(r.path,s),LP(s,o,{listPrs:()=>cn(r.path),merge:a=>Is(r.path,a,{method:t}),sleep:DP,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function jP(e,t){let n=Date.now()+(t.settleMaxMs??PP),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(NP)}}async function LP(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 jP(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: ${ln(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(!Vp(a)){e.skipped.push({number:a.number,title:a.title,reason:BP(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:ln(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),un()}}async function zp(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Es("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ln(l)}`)}await EP(e);let o=n||"";if((await tn({cwd:e})).GH_TOKEN){let l=await Cd().catch(()=>null);l&&(o=`${o?`${o}
|
|
180
|
-
|
|
181
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await
|
|
150
|
+
`});function OM(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 MM(e,t,n){let r=lk(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function wr(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,f=(y,m)=>{u.set(y,m),t.push(m)},d=y=>{let m=u.get(y);m&&(m.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,h=new Map;for(let y of e||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let m=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let b of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let M=b[0].split("/").pop().toLowerCase();h.has(M)||h.set(M,[]),h.get(M).push(y.payload?.ts??y.ts)}}let w=3*6e4,g=20*6e4,k=(y,m)=>!!(y?.name&&h.get(String(y.name).toLowerCase()))?.some(M=>M>=m-w&&M<=m+g);for(let y of e||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let m=Ue(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=Ue(y.payload?.text);m&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(m))}else if(y.type==="job.started"&&y.payload?.id)a.add(y.payload.id),(y.payload.kind||"agent")==="agent"&&y.payload.name&&c.add(y.payload.name);else if(y.type==="turn.user"&&y.payload?.note?.kind==="task"){let m=y.payload.note,b={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:fe(y)};m.toolUseId&&i.set(m.toolUseId,b);let M=String(m.summary||"").match(p);M&&l.set(M[1],b)}let S=new Map,A=new Map,R=null,B=new Map,T=new Map,_=new Map,O=new Map,I=new Map,U=new Map,H=new Map,ae=new Map,J=new Map,C=new Map,Q=new Map,ve=new Map,W=new Map,be=new Map,se=new Map,ue=new Map,me=new Map,j=new Map,ie=new Map,le=new Map,Ae=new Map,$e=()=>{for(let y of Ae.values())y.questions.length&&t.push(y);Ae.clear()},He=null,ze=6e4,E=new Map,ne=(y,m)=>{let b=o.get(y);if(!b?.length)return!1;let M=Ue(m),X=s.get(y);return b.some(he=>{if(!M.startsWith(he))return!1;let De=Ue(M.slice(he.length));return!!De&&!!X?.has(De)})};for(let y of e||[]){let m=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length)break;{let b=m.note||Tl(m.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let M=String(b.summary||"").match(p);if(!b.toolUseId&&M&&c.has(M[1]))break}if(b.kind==="command"&&nk(b.command))break;if(b.kind==="resume-summary"){t.push({id:y.id,kind:"resume-summary",title:b.title||"Context resumed from summary",bullets:b.bullets||[],sections:b.sections||[],rawChars:b.rawChars??null,ts:fe(y),box:y.boxId,agent:y.provider});break}b.kind!=="suppress"&&t.push({id:y.id,kind:"event",type:ok(b),name:b.name??null,command:b.command??null,summary:b.summary??null,status:b.status??null,ts:fe(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${fe(y)}`)||y.source==="transcript"&&ne(y.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){t.push({id:y.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:fe(y),box:y.boxId,agent:y.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"){m.kind==="prompt-answer"&&(He=null);break}for(let b of B.values())b.status="stale";B.clear();for(let b of _.values())b.status="stale";_.clear();for(let b of W.values())b.status="stale";W.clear(),He&&He.box===y.boxId&&(t.push(OM(He)),He=null),t.push({id:y.id,kind:"message",role:"user",text:m.text,ts:fe(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...y.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let b=m.from||{};t.push({id:y.id,kind:"event",type:"peer-message",text:m.text,from:b.trackName||b.boxId||b.name||null,fromTrackId:b.trackId||null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let b=yb(m,{codexFailedText:ao});if(b){t.push({id:y.id,kind:"event",type:"provider-error",cls:b.cls,status:b.status??void 0,text:b.text,provider:y.provider,legacy:!0,at:fe(y),ts:fe(y),box:y.boxId,agent:y.provider});break}$e();let M=(m.images||[]).filter(Ye=>!k(Ye,fe(y)));if(!String(m.text||"").trim()&&!M.length)break;if(y.provider==="cursor"){let Ye=`${y.boxId}|${m.text}`;if(n.has(Ye))break;n.add(Ye)}(!He||He.box===y.boxId)&&(He=null);let X=y.provider==="codex"?(m.tools||[]).filter(Ye=>!Af.has(Ye)&&!Va.has(Ye)):m.tools||[],he=m.text,De=null;if(zf.has(y.provider)){let Ye=Yf(String(m.text||""));Ye&&(he=Ye.text,De={questions:Ye.questions})}t.push({id:y.id,kind:"message",role:"assistant",text:he,ts:fe(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...De?{ask:De}:{},...X.length?{tools:X}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...M.length?{images:M}:{}}),De&&(He={id:y.id,groupId:Jf(y.provider,m.sourceId||y.id,De.questions),provider:y.provider,box:y.boxId,questions:De.questions,ts:fe(y)});break}case"job.started":{if(!m.id)break;let b=fe(y),M=i.get(m.id);if(!M&&(m.kind||"agent")==="agent"&&m.name){let tt=l.get(m.name);tt&&tt.ts>=b&&(M=tt)}let X={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:M?M.status==="killed"?"stopped":M.status&&M.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:M?.ts??null,summary:M?.summary??null,resultHead:M?.resultHead??null},he=E.get(y.boxId),De=he?.jobs[he.jobs.length-1]?.startedTs;if(he&&De!=null&&b-De<=ze)he.jobs.push(X);else{let tt={id:y.id,kind:"job-dock",dockId:m.id,ts:b,box:y.boxId,agent:y.provider,jobs:[X]};E.set(y.boxId,tt),t.push(tt)}break}case"handoff.done":t.push(dt(y,"lead-handoff"));break;case"subtask.delegated":t.push({...dt(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":t.push(dt(y,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let M of B.values())M.status="stale";B.clear();for(let M of _.values())M.status="stale";_.clear()}let b=dt(y,`pr-${m.change}`);if(b.box=y.boxId??null,CM.has(m.change)&&m.number!=null){let M=MM(S,A,m);if(S.size>=2){for(let he of A.values())for(let De of he)De.grouped=!0;b.groupPrs=[...S.values()];let X=[b];for(let he of S.keys())A.set(he,X)}else{let X=A.get(M)??[];X.push(b),A.set(M,X)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let M=lk(m),X=R&&!R.complete||S.size>=2;if(S.has(M))S.delete(M),A.delete(M);else if(m.number!=null)for(let he of[...S.keys()])he.endsWith(`#${m.number}`)&&(S.delete(he),A.delete(he));if(X&&m.number!=null){(!R||R.complete)&&(R={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:fe(y),ts:fe(y),box:y.boxId,agent:y.provider},t.push(R)),R.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:fe(y)}),R.remaining=[...S.values()],R.complete=S.size===0;break}}t.push(b);break}case"pr.offered":{if(!m.offerId)break;for(let M of B.values())M.status="stale";B.clear();for(let M of _.values())M.status="stale";_.clear();let b={id:y.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:y.source==="cli"||m.via==="cli"?"cli":"transcript",ts:fe(y),box:y.boxId,agent:y.provider};B.set(m.offerId,b),T.set(m.offerId,b),t.push(b);break}case"pr.offer.answered":{let b=T.get(m.offerId);b&&(b.status=m.action||"dismiss",b.actions=Array.isArray(m.actions)?m.actions:null,b.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(b.existingPr={number:m.existingPr.number??null}),B.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let M of _.values())M.status="stale";_.clear();let b={id:y.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};_.set(m.offerId,b),O.set(m.offerId,b),t.push(b);break}case"pr.exists.answered":{let b=O.get(m.offerId);b&&(b.status=m.choice||"dismiss",_.delete(m.offerId));break}case"box.state.changed":{let b=m.change==="model"?"model-change":m.change;if(!b)break;let M=dt(y,b);if(b==="model-change"&&(M.to=m.model),b==="node-update"&&M.latest){let X=t.findIndex(he=>he.kind==="event"&&he.type==="node-update"&&he.latest===M.latest);if(X>=0){let he=t[X];t[X]={...M,ts:he.ts,at:he.at,firstAt:he.firstAt??he.ts,updatedAt:M.ts};break}}t.push(M);break}case"track.transferred":case"track.recovered":t.push(dt(y,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){d(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;f(`view:${m.viewId}`,{id:y.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:fe(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{t.push({id:y.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:fe(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(m.change==="picked"){t.push({id:y.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:fe(y),box:y.boxId,agent:y.provider});break}if(m.change==="deleted"){d(`mock:${m.mockId}`);break}if(m.change!=="created")break;f(`mock:${m.mockId}`,{id:y.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(m.change==="deleted"){d(`flow:${m.flowId}`);break}if(m.change!=="created")break;f(`flow:${m.flowId}`,{id:y.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:fe(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){d(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;f(`runbook:${m.runbookId}`,{id:y.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:fe(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{t.push({id:y.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let M of H.values())M.box===(m.box||y.boxId)&&(M.status="stale",H.delete(M.offerId));let b={id:y.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:fe(y),box:m.box||y.boxId,agent:m.agent||y.provider};H.set(m.offerId,b),ae.set(m.offerId,b),t.push(b);break}case"box.fullscreen.answered":{let b=ae.get(m.offerId);b&&(b.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",H.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let M of I.values())M.key===m.key&&(M.status="stale",I.delete(M.offerId));let b={id:y.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};I.set(m.offerId,b),U.set(m.offerId,b),t.push(b);break}case"ticket.move.answered":{let b=U.get(m.offerId);b&&(b.status=m.action==="moved"?"moved":"dismiss",I.delete(m.offerId));break}case"ticket.state.changed":{for(let b of I.values())b.key===m.key&&b.to===m.to&&(b.status="stale",I.delete(b.offerId));if(m.mode==="offer")break;t.push(dt(y,"ticket-state"));break}case"ticket.commented":{t.push({id:y.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":t.push(dt(y,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let M of J.values())M.status="stale";J.clear();let b={id:y.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};J.set(m.proposalId,b),C.set(m.proposalId,b),t.push(b);break}case"ticket.create.answered":{let b=C.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",b.createdCount=m.createdCount??null,b.failedCount=m.failedCount??null,J.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let M of Q.values())M.status="stale";Q.clear();let b={id:y.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};Q.set(m.proposalId,b),ve.set(m.proposalId,b),t.push(b);break}case"loop.create.answered":{let b=ve.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",Q.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let b=m.replaces?se.get(m.replaces):null;b&&b.status==="armed"&&(b.status="replaced");let M={id:y.id,kind:"followup-armed",armId:m.armId,trigger:m.trigger??"now",label:m.label??"",prompt:m.prompt??"",why:m.why??null,status:"armed",ts:fe(y),box:y.boxId,agent:y.provider};se.set(m.armId,M),M.trigger!=="now"&&t.push(M);break}case"followup.armed.cancelled":{let b=se.get(m.armId);b&&b.status==="armed"&&(b.status="cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let X of W.values())X.trigger===m.trigger&&(X.status="stale",W.delete(X.offerId));let b=Array.isArray(m.rows)?m.rows:[];for(let X of b){let he=X.armId?se.get(X.armId):null;he&&he.status==="armed"&&(he.status="offered")}let M={id:y.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},rows:b.map(X=>({followupId:X.followupId??null,armId:X.armId??null,label:X.label??"",prompt:X.prompt??null,promptPreview:X.promptPreview??null,promptHash:X.promptHash??null,origin:X.origin??"operator",why:X.why??null})),status:"open",answer:null,ts:fe(y),box:y.boxId,agent:y.provider};W.set(m.offerId,M),be.set(m.offerId,M),t.push(M);break}case"followup.answered":{let b=be.get(m.offerId);b&&(b.status=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss",b.answer={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0},W.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let M of ue.values())M.status="stale";ue.clear();let b={id:y.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:fe(y),box:y.boxId,agent:y.provider};ue.set(m.proposalId,b),me.set(m.proposalId,b),t.push(b);break}case"followup.create.answered":{let b=me.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",ue.delete(m.proposalId));break}case"followup.created":{let b=m.proposalId?me.get(m.proposalId):null;b&&(b.created={followupId:m.followupId??null});break}case"loop.created":{let b=m.proposalId?ve.get(m.proposalId):null;b&&(b.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(y,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let X of j.values())X.loopId===m.loopId&&(X.status="stale",X.staleReason="superseded",j.delete(X.proposalId));let b=y.type==="loop.update.offered",M={id:y.id,kind:b?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...b?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:fe(y),box:y.boxId,agent:y.provider};j.set(m.proposalId,M),ie.set(m.proposalId,M),t.push(M);break}case"loop.update.answered":{let b=ie.get(m.proposalId);b&&(m.action==="moved"?(b.status="stale",b.staleReason="moved"):b.status=m.action==="applied"?"applied":"dismiss",j.delete(m.proposalId));break}case"loop.delete.answered":{let b=ie.get(m.proposalId);b&&(m.action==="moved"?(b.status="stale",b.staleReason="moved"):b.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",j.delete(m.proposalId));break}case"loop.updated":{let b=m.proposalId?ie.get(m.proposalId):null;b&&(b.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(y,"loop-updated"));break}case"loop.deleted":t.push(dt(y,"loop-deleted"));break;case"loop.paused":t.push(dt(y,"loop-paused"));break;case"rework.cause.offered":{if(!m.sha||le.has(m.sha))break;let b={id:y.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,status:"open",cause:null,ts:fe(y),box:y.boxId,agent:y.provider};le.set(m.sha,b),t.push(b);break}case"rework.cause.answered":{let b=m.sha?le.get(m.sha):null;b&&(b.status="answered",b.cause=m.cause??null);break}case"ticket.created":{let b={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},M=m.proposalId?C.get(m.proposalId):null;if(M){M.created=b;break}t.push({id:y.id,kind:"ticket-created",...b,ts:fe(y),box:y.boxId,agent:y.provider});break}case"error":{if(!m.kind)break;t.push(dt(y,m.kind));break}case"provider.error":t.push({...dt(y,"provider-error"),provider:y.provider,box:y.boxId,agent:y.provider});break;case"usage.cap.hit":t.push(dt(y,"usage-cap-hit"));break;case"usage.cap.warning":t.push(dt(y,"usage-cap-warning"));break;case"provider.connected":{t.push({id:y.id,kind:"event",type:"provider-connected",provider:m.provider||y.provider,ts:fe(y),at:fe(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(He=null,m.control||!m.question)break;let b=m.groupId||m.promptId;if(!b)break;let M=Ae.get(b);M||(M={id:y.id,kind:"prompt",groupId:b,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:m.questionTotal??1,questions:[],ts:fe(y)},Ae.set(b,M));let X=m.questionIndex??0,he={index:X,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},De=M.questions.findIndex(tt=>tt.index===X&&Ue(tt.question)===Ue(he.question));De>=0?M.questions[De]=he:M.questions.push(he),M.questions.sort((tt,rr)=>tt.index-rr.index),M.questionTotal=Math.max(M.questionTotal,m.questionTotal??1),M.questions.length>=M.questionTotal&&(t.push(M),Ae.delete(b));break}default:break}}let Ke=u.size?t.filter(y=>!y.retired):t;return PM(Ke.sort((y,m)=>(y.ts??0)-(m.ts??0)))}function PM(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<=NM){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 Qf(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:DM(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(t??wr(e)).filter(o=>o.kind==="message");return ub(n,r)}function DM(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ps(e.command)?t:mr(e.command,e.description||"")}function dt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:fe(e),ts:fe(e)}}function 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)+BM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&jM.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function dk(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!FM.has(r.type)||!r.box)continue;let o=(r.ts??0)+LM,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 fk(e){return(e||[]).filter(t=>UM.has(t?.kind)&&t.status==="open")}var fe,CM,lk,NM,BM,jM,LM,FM,UM,ep=L(()=>{ms();io();ul();yl();Vf();Zf();fe=e=>e.payload?.ts??e.ts;CM=new Set(["opened","ci-fixed","reconciled","pushed"]),lk=e=>`${e.repo??""}#${e.number}`;NM=2*6e4;BM=12e4,jM=new Set(["archived","unarchived","opened"]);LM=2500,FM=new Set(["pr-opened","pr-merged","pr-auto-armed"]);UM=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 GM(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 zM(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 YM(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<qM&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<KM&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function wk(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=GM(e),s=zM(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=wr(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-HM);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(hk[r]??hk.failover),l.push(""),o.lastPush?.sha){let d=mk(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=mk(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=YM(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"}:** ${VM(d.text,WM)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
|
|
151
|
+
`);return f.length<=pk?f:`${f.slice(0,pk)}
|
|
152
|
+
|
|
153
|
+
[\u2026 brief truncated]`}var HM,WM,pk,qM,KM,VM,mk,hk,gk=L(()=>{ep();HM=12,WM=2e3,pk=48e3,qM=30,KM=10;VM=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},mk=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;hk={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 JM from"node:fs";import gr from"node:fs/promises";import yr from"node:path";function Ek(e){let t=no(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=yk?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,yk).toString("utf8").replace(/�+$/,"")}
|
|
154
|
+
|
|
155
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function sN(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(kk.test(s)){if(r.length>=Ok)continue;let a=oN(s.replace(kk,"").replace(/[.;,:]+$/,""),Ck,nN);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>bk&&(o=`${o.slice(0,bk-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Mk(e,t,{timeoutMs:n=9e4}={}){let r=yr.join(t,eN),o=yr.join(r,tN);await gr.mkdir(r,{recursive:!0});let s=await El(o),a=await ge(e);if(!a)throw new Error(`session "${e}" not found`);return await Al(e,a,rN(o)),await Fk(o,s,n),sN(await gr.readFile(o,"utf8"))}function Nk(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===_k||n===Rk;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(Ak)||n===XM||r.startsWith(Ik)||r.startsWith($k)}function Pk(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-aN;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 Dk(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 Bk(e,{windows:t=[]}={}){if(Nk(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 _l(e){return tp.get(e)||null}async function jk(e){let t=tp.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ge(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:yr.join(n.cwd,Sk,Tk)};return tp.set(e,r),lN(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function lN(e,t){e.step="summarize",await Lk(e.name,t),e.step="clear";let n=await ge(e.name);await Al(e.name,n,"/clear"),await op(iN),e.step="resume",await Al(e.name,n,QM),e.step="done",e.finishedAt=Date.now()}async function Lk(e,t,{timeoutMs:n=cN}={}){let r=yr.join(t,Sk),o=yr.join(r,Tk);await gr.mkdir(r,{recursive:!0});let s=await El(o),a=await ge(e);await re({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Al(e,a,ZM(o)),await Fk(o,s,n),await re({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await El(o)});let i=await Yb(t,o,{exclude:u=>Nk(u.role,u.text)});await re({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await $({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await gr.readFile(o,"utf8");await $({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Ek(u),ts:Date.now()}})}catch{}}await Qd(t);let l=await uN(r,o);return c&&await Cy(c,l||o,{box:e,agent:a?.agent??null}),o}async function np(e,{timeoutMs:t}={}){let n=await ge(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return Lk(e,n.cwd,t?{timeoutMs:t}:void 0)}async function rp(e,t,{trackName:n="this Track"}={}){let r=await F({id:e,cwd:t}),o=wk(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=yr.join(t,xk);return await gr.mkdir(yr.dirname(s),{recursive:!0}),await gr.writeFile(s,o,"utf8"),await $({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Ek(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),xk}async function El(e){try{return(await gr.stat(e)).mtimeMs}catch{return 0}}async function Fk(e,t,n){let r=Date.now()+n;for(;;){if(await El(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await op(2e3)}}async function uN(e,t){try{if(!JM.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=yr.join(e,`${n}.md`);return await gr.copyFile(t,r),r}catch{return null}}function op(e){return new Promise(t=>setTimeout(t,e))}function vk(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 Al(e,t,n){if(t){let o=vk(t.agent);await hr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ge(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Be(e,["Escape"],{kind:"drive-wipe"}),await op(150),await ls(e,il,{kind:"drive-wipe"}),await Qe(e,n,{kind:"orchestration-drive",...un(r.agent,n)}),await ti({sessionName:e,agent:r.agent,agentCmd:vk(r.agent),text:n,flow:"orchestration-drive"})}var Sk,Tk,yk,Ak,_k,Ik,XM,ZM,QM,$k,Rk,eN,tN,bk,Ok,Ck,nN,rN,kk,oN,aN,iN,cN,tp,xk,sp=L(()=>{lt();Xa();cn();Za();Uf();Jb();Ra();de();Zc();gk();Sk=".bostrat/handoff",Tk="latest.md",yk=32*1024;Ak="Checkpoint: write a work-in-progress handoff",_k="HANDOFF WRITTEN",Ik="Resume from .bostrat/handoff/latest.md",XM="/clear",ZM=e=>`${Ak} 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 ${_k} and take no further action.`,QM=`${Ik}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,$k="Beat: write a",Rk="BEAT WRITTEN",eN=".bostrat/arc",tN="beat.txt",bk=200,Ok=4,Ck=5,nN=36,rN=e=>`${$k} 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 ${Ok} further lines, each starting with "- " and naming ONE piece in at most ${Ck} 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 ${Rk} and take no further action.`,kk=/^(?:[-–—*•·]|\d+[.)])\s+/,oN=(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};aN=6e5;iN=3500,cN=3e5,tp=new Map;xk=".bostrat/handoff/status-latest.md"});function kr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Il.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 Wk(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:kr(o.from,"from",n),toMs:kr(o.to,"to",n)}))}return t}function ip(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 fN(e){if(!e)return null;let t=String(e).trim();return br[t]?t:Uk[t]?Uk[t]:Object.keys(br).find(r=>t.startsWith(r))||null}function pN(e){if(!e||e===br)return dN;let t=Hk.get(e);return t||(t=Wk(e),Hk.set(e,t)),t}function qk(e,t,n={}){let{table:r,speed:o}=n,s=pN(r),a=r&&r!==br?s[e]?e:null:fN(e);if(!a)return null;let i=ip(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 dn(e,t={},n,r){let o=qk(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 mN(e=br){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:kr(a.from,"from",n),toMs:kr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),Il.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 wN({now:e=Date.now(),table:t=br,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...hN,...n},a=[],i=ip(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(f=>({...f,fromMs:kr(f.from,"from",c),toMs:kr(f.to,"to",c),verifiedMs:Il.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*ap;d&&i-f.verifiedMs>r*ap&&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*ap&&a.push(`${c}: scheduled rate change on ${f.from} was last verified ${f.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function bs({now:e=Date.now(),table:t=br,...n}={}){let r=ip(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=kr(l.from,"from",i),f=kr(l.to,"to",i);r>=u&&r<=f&&Il.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=[...mN(t),...wN({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var br,Uk,Il,dN,Hk,Kk,hN,ap,Pn=L(()=>{br={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Uk={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"},Il=/^\d{4}-\d{2}-\d{2}$/;dN=Wk(br);Hk=new WeakMap;Kk=(e,t,n)=>qk(e,t,n)!==null;hN=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),ap=864e5});function gN(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Vk(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Bt(e){let t=e.payload?.model||"unknown",n=gN(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=dn(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&&!Kk(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function xr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=Gk();for(let d of n){let{usd:p,model:h,tokens:w,repriced:g,unpriced:k,asRecorded:S}=Bt(d);i+=S;let A=d.provider||"unknown",R=d.boxId||"unattributed";if(g&&c++,k&&(l++,u.add(h)),a+=p,Vk(f,w),r[h]||={usd:0,tokens:Gk(),events:0},r[h].usd+=p,r[h].events+=1,Vk(r[h].tokens,w),o[A]||={usd:0,events:0},o[A].usd+=p,o[A].events+=1,s[R]||={usd:0,events:0,provider:A,models:new Set,label:t[R]||R},s[R].usd+=p,s[R].events+=1,h!=="unknown"&&s[R].models.add(h),d.epoch!=null){let B=(s[R].epochs||={})[d.epoch]||={usd:0,events:0,provider:A,models:new Set};B.usd+=p,B.events+=1,B.provider=A,h!=="unknown"&&B.models.add(h)}}return{totalUsd:ks(a),asRecordedUsd:ks(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:$l(r,d=>({...d,usd:ks(d.usd)})),byProvider:$l(o,d=>({...d,usd:ks(d.usd)})),byBox:$l(s,d=>({usd:ks(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:$l(d.epochs,p=>({usd:ks(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Gk,ks,$l,vr=L(()=>{Pn();Gk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});ks=e=>Math.round(e*1e4)/1e4,$l=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function yN(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 zk(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(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(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${cp(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=xr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?yN(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var cp,Yk=L(()=>{vr();cp=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as bN}from"node:child_process";import{promisify as kN}from"node:util";import{randomUUID as xN}from"node:crypto";async function lp(e,t){let n=Date.now()+t;for(;;){if(await Cn(e))return!0;if(Date.now()>n)return!1;await ai(SN)}}async function EN(e,t){let n=Xk[t]||Xk.default;n.escape&&(await Be(e,["Escape"],{kind:"swap-exit"}),await ai(150)),n.wipe&&await ls(e,il,{kind:"swap-exit"}),await cl(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=un(t).submitDelayMs||0;await ai(Math.max(r,250)),await Be(e,["Enter"],{kind:"swap-exit"})}async function Zk(e,t){try{let{stdout:n}=await lo(K,G("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Qk(e){await lo(K,G("respawn-pane","-k","-t",e))}async function up(e,t={}){if(!Ne(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ge(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||dr(n),a=Number(t.exitTimeoutMs)||vN;await re({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Cn(e)&&(await EN(e,o),!await lp(e,a)&&(i=!0,await re({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Qk(e),!await lp(e,Jk))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&ro(n),l=c?Mc():null,u=await Zk(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await lo(K,G("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await lo(K,G("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?tl(g,l):g}if(f&&!i&&(await Qk(e),!await lp(e,Jk)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,p=t.resumeSessionId||xN(),h=d?n:Fa(n,p,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await or(r.cwd),s==="codex"&&await Jo(r.cwd),s==="cursor"&&await Xo(r.cwd),await oo(e),await lo(K,G("set-option","-t",e,"@bostrat-sid",p)),await lo(K,G("set-option","-t",e,"@bostrat-agent",s)),await ai(TN),c&&La().length&&(await Zk(e,ja)||await lo(K,G("set-environment","-t",e,ja,"1")).catch(()=>{}),await Qe(e,`export ${ja}=1`,{kind:"swap-env"}),await ai(150)),await Qe(e,h,{kind:"swap-launch"}),await re({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:f,launchCmd:h}}var lo,ai,vN,Jk,SN,TN,Xk,e0=L(()=>{lt();Sa();Pc();Dc();Za();cn();lo=kN(bN),ai=e=>new Promise(t=>setTimeout(t,e)),vN=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Jk=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,SN=250,TN=400,Xk={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 t0(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await St(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
156
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let p=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let p=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(p?ps(p):null),detail:p?ps(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&AN.test(_N(d.content))&&s.add(d.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var AN,_N,n0=L(()=>{zt();ms();AN=/^Async (?:agent|task) launched/i,_N=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 Sr(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 IN(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 r0(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:Yt(`read ${Sr(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${xs(r)}`:`Read ${xs(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Sr(t.path,n):null,s=t?.glob?` --glob ${dp(t.glob)}`:"",a=Yt(String(r),40);return{command:Yt(`grep ${dp(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?Sr(t.target_directory,n):null;return{command:Yt(`glob ${dp(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Yt(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Yt(`edit ${Sr(t.path,n)}`,140),description:`Edit ${xs(t.path)}`}:null;case"Write":return t?.path?{command:Yt(`write ${Sr(t.path,n)}`,140),description:`Write ${xs(t.path)}`}:null;case"Delete":return t?.path?{command:Yt(`delete ${Sr(t.path,n)}`,140),description:`Delete ${xs(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Sr(s,n)).filter(Boolean);return{command:Yt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=IN(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Yt(`apply_patch ${Sr(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${xs(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:Yt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Rl,Yt,xs,dp,o0=L(()=>{Rl=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Yt=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,xs=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);dp=e=>`"${String(e).replace(/"/g,'\\"')}"`});import ii from"node:fs";import $N from"node:fs/promises";import uo from"node:path";import RN from"node:os";import ON from"node:crypto";import CN from"node:zlib";function i0(e){let t=ON.createHash("md5").update(String(e)).digest("hex");return uo.join(MN,t)}function c0(e){try{let t=JSON.parse(ii.readFileSync(uo.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 NN(e){let t=i0(e),n;try{n=ii.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=uo.join(t,o.name),a=uo.join(s,"store.db");if(!ii.existsSync(a))continue;let i=c0(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 Ol(e,t){if(!e)return null;if(t){let n=uo.join(i0(e),t),r=uo.join(n,"store.db");if(ii.existsSync(r))return{id:t,dir:n,store:r,...c0(n)||{createdAtMs:null,updatedAtMs:null}}}return NN(e)}function s0(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function PN(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=CN.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function DN(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 BN(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Rl.has(r.toolName))continue;let o=r0(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 fp(e,t){if(!a0)return vs;let{DatabaseSync:n}=a0,r;try{r=new n(e,{readOnly:!0})}catch{return vs}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 vs;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return vs}let a=s.lastUsedModel||null,i=s.latestRootBlobId?s0(r,s.latestRootBlobId):null;if(!i)return{...vs,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of DN(i)){let h=PN(s0(r,p));if(!h)continue;let w=h.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),h.role!=="assistant")continue;if(Array.isArray(h.content))for(let S of h.content){let A=S?.providerOptions?.cursor?.modelName;A&&(f=A)}l.push(...BN(h.content,t).map((S,A)=>({...S,id:S.id||`${p}:${A}`}))),u.push(...LN(h.content));let g=jN(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:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return vs}finally{try{r.close()}catch{}}}function l0(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(ii.statSync(e.store).mtimeMs)}catch{n=Date.now()}let r=e.createdAtMs;(!r||r>n)&&(r=n-t*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/t)}}async function u0(e,t,{limit:n=80}={}){let r=Ol(e,t);if(!r)return[];let{turns:o,model:s}=await fp(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=l0(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 d0(e,t){let n=Ol(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await fp(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 f0(e,t,{limit:n=80}={}){let r=Ol(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await fp(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=l0(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:mr(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:mr(u.command,u.description),ts:i+1});return l}async function p0(e,t){let n=Ol(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await $N.stat(n.store)).mtimeMs)}catch{return null}}var MN,jN,LN,a0,vs,pp=L(()=>{ms();o0();MN=process.env.BOSTRAT_CURSOR_CHATS||uo.join(RN.homedir(),".cursor","chats");jN=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():"",LN=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],a0=process.getBuiltinModule?.("node:sqlite"),vs={turns:[],trailingCommands:[],model:null,requestIds:[]}});import FN from"node:fs/promises";function gp(e){return m0.set(e.agent,e),e}function ci(e){return m0.get(e)||UN}async function bp(e,t={}){return(await ci(e.agent).readMessages(e,t)).map(yp(e))}async function h0(e,t={}){return(await ci(e.agent).readCommands(e,t)).map(yp(e))}async function w0(e,t={}){return(await ci(e.agent).readJobs(e,t)).map(yp(e))}var Ss,mp,hp,wp,UN,m0,yp,g0=L(()=>{zt();dl();ms();n0();pp();io();Ss=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}},mp=class extends Ss{transcript(t){return!t?.cwd||!t?.sessionId?null:Vt(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?If(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?lb(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?t0(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await FN.stat(n)).mtimeMs)}catch{return null}}},hp=class extends Ss{async readMessages(t,n){return u0(t.cwd,t.sessionId,n)}async readCommands(t,n){return f0(t.cwd,t.sessionId,n)}async updatedAt(t){return p0(t.cwd,t.sessionId)}},wp=class extends Ss{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return pb(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return mb(t.cwd,this.sessionId(t),n)}async updatedAt(t){return hb(t.cwd,this.sessionId(t))}},UN=new Ss("unknown"),m0=new Map;gp(new mp("claude",Sf));gp(new hp("cursor",Rl));gp(new wp("codex",Va));yp=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function mo(e){let t=e.search(Sp);return(t>=0?e.slice(0,t):e).trim()}function HN(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function WN(e){let t=String(e).trim();return!!(!t||po.test(e)||Sp.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function qN(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=KN(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function KN(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(d=>d.length>3&&n.includes(d)).length;if(u)return u;let f=l.slice(0,8).trim();return f.length>=4&&n.includes(f)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function b0(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function GN(e,t=""){let n=String(e);if(!x0.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
158
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let f=r[u].match(fo);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!po.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:mo(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 zN(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||!v0.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(fo);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&&!(VN.test(n[c])||po.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:mo(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 YN(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(fo);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 XN(e,t=""){let n=String(e);if(!kp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
161
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(JN.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let f=r[u].match(fo);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!po.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>kp.test(u.label)))return null;let i=s.map(u=>({label:mo(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 Tp(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(fo);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 ZN(e,t=""){let n=String(e);if(!S0.test(n)||!T0.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=Tp(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:mo(s.label),description:""}))}]}:null}function QN(e,t=""){let n=String(e);if(!E0.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=Tp(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:mo(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 tP(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!eP.test(o)||Ep.test(e[r]))break;n+=o}return n}function nP(e,t=""){let n=String(e);if(!Ep.test(n)||ho.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=tP(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 oP(e,t=""){let n=String(e);if(!xp.test(n)||!rP.test(n)||ho.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
165
|
+
`)].reverse().find(a=>xp.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 sP(e,t=""){let n=String(e);if(!A0.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o=Tp(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:mo(s.label),description:""}))}]}:null}function aP(e,t=""){let n=String(e);if(ho.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(_0);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(po.test(r[l])||Sp.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 iP(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(vp.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 cP(e,t=""){let n=String(e);if(!k0.test(n)||!vp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
|
+
`),o=iP(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let O=i;O<c;O++){let I=r[O].match(fo);I&&(u<0&&(u=O),l.push({line:O,cursor:!!I[1],checked:I[3]!=null&&I[3]!==" ",hasCheckbox:I[3]!=null,label:I[4].trim(),desc:""}))}if(!l.length)return null;for(let O of l)for(let I=O.line+1;I<Math.min(r.length,O.line+4)&&!(fo.test(r[I])||po.test(r[I]));I++){let U=r[I].trim();if(!(!U||WN(r[I])||y0.test(U))){O.desc=HN(U);break}}let f=l.filter(O=>!y0.test(O.label));if(!f.length)return null;let d=f.some(O=>O.hasCheckbox),p=Math.max(0,f.findIndex(O=>O.cursor)),h=s>=0?s+1:0,w=[];for(let O=h;O<u;O++){let I=r[O].trim();I&&!po.test(r[O])&&!/[☐☒]/.test(r[O])&&w.push(I)}let g=w.join(" ")||(s>=0?b0(r[s]):"")||"The agent is asking a question",k=s>=0?qN(r[s],g):null,S=k?.currentTabLabel||(s>=0?b0(r[s]):""),A=f.map(O=>({label:mo(O.label),description:O.desc})),R=A.map(O=>O.label).join("|"),T=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${S}:${g}:${R}`.slice(0,240),_=k?`claude:ask:${k.tabs.map(O=>O.label).join("|")}`.slice(0,240):T;return{provider:"claude",box:t,multiSelect:d,cursorIndex:p,promptId:T,groupId:_,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:g,header:S,multiSelect:d,options:A}]}}function wo(e,t){let n=String(t||"");return n?e==="claude"?k0.test(n)&&vp.test(n)||x0.test(n)||kp.test(n)||v0.test(n.trimEnd().split(`
|
|
169
|
+
`).pop()||"")?!0:ho.test(n)?!1:S0.test(n)&&T0.test(n)||E0.test(n)||Ep.test(n)||xp.test(n)||A0.test(n)||_0.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 Tr(e,t){return!e||!t?null:e.agent==="claude"?YN(t,e.name)||cP(t,e.name)||GN(t,e.name)||XN(t,e.name)||oP(t,e.name)||nP(t,e.name)||QN(t,e.name)||ZN(t,e.name)||sP(t,e.name)||aP(t,e.name)||zN(t,e.name):e.agent==="cursor"||e.agent==="codex"?Rb(e.agent,t,e.name):null}async function Ap(e){if(!e)return null;let t=await Mn(e.name).catch(()=>"");return Tr(e,t)}function I0(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 $0(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 k0,vp,fo,y0,po,Sp,x0,v0,VN,kp,JN,ho,S0,T0,E0,Ep,eP,xp,rP,A0,_0,_p=L(()=>{lt();pl();k0=/to navigate\b.*\bEsc to cancel|Enter to select\b/,vp=/Chat about this/i,fo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,y0=/^(type something|chat about this|submit)\b/i,po=/^[─—-]{6,}\s*$/,Sp=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;x0=/^\s*Switch model\?\s*$/m;v0=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,VN=/^\s*[─—▔-]{6,}\s*$/;kp=/Resume from summary/i,JN=/^\s*This session is\b/;ho=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;S0=/Choose the text style that looks best with your terminal/,T0=/Let's get started\./;E0=/Select login method:/;Ep=/Paste code here if prompted/,eP=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;xp=/OAuth error:/,rP=/Press Enter to retry\./;A0=/Quick safety check: Is this a project you created or one you trust/;_0=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Cl(e,t,n){if(!e||!t)return null;for(let r of lP)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function dP(e){let t=Ip.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},Ip.set(e,t)),t}function C0(e,t,n=Date.now()){let r=dP(e);if(r.sentAt=r.sentAt.filter(s=>n-s<O0),r.sentAt.length>=R0)return{action:"give-up"};if(n-r.last<uP)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 M0(e,t=Date.now()){let n=Ip.get(e);return n?n.sentAt.filter(r=>t-r<O0).length>=R0:!1}var lP,uP,R0,O0,Ip,$p=L(()=>{lP=[{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"]}];uP=5e3,R0=5,O0=3600*1e3,Ip=new Map});function Ts(e){return typeof e=="string"?e.trim():""}var N0=L(()=>{});import{execFile as fP}from"node:child_process";import{promisify as pP}from"node:util";async function Le(e,t,{input:n}={}){let r=Op("git",["-C",e,...t],{maxBuffer:P0});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Es(e){if(!e)return!1;try{let{stdout:t}=await Le(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function D0(e){let{stdout:t}=await Le(e,["rev-parse","--show-toplevel"]);return t.trim()}async function mP(e){try{return await Le(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function As(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Le(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Op("git",["-C",e,...a],{maxBuffer:P0,env:o,...i});try{let a=await mP(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 Op("rm",["-f",r])}catch{}}}async function Cp(e,t,{trailers:n=[]}={}){let{stdout:r}=await Le(e,["status","--porcelain"]);return r.trim()?(await Le(e,["add","-A"]),await Le(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function li(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Le(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 ui(e,t){if(!t)return 0;try{let{stdout:n}=await Le(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function _s(e,t,n,r){return await Le(e,["worktree","add","--no-track","-b",n,t,r]),t}async function B0(e,t){try{return await Le(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function Ml(e,t,n){return await Le(e,["worktree","add",t,n]),t}async function Is(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Le(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 Le(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Mp(e){await Le(e,["init","-q"]),await Le(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 Le(e,["worktree","remove","--force",t])}catch{try{await Le(e,["worktree","prune"])}catch{}}}function hP(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 Np(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Pp(e,t,n,r){let{stdout:o}=await Le(e,Np(["diff","--shortstat",t,n],r));return hP(o)}async function Dp(e,t,n,r){let{stdout:o}=await Le(e,Np(["diff","--name-only",t,n],r));return o.split(`
|
|
173
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Bp(e,t,n,r){let{stdout:o}=await Le(e,Np(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Rp(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Rp(e,o,["--3way","--whitespace=nowarn"]),a=await wP(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Rp(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Rp(e,t,n){try{return await Le(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function wP(e){try{let{stdout:t}=await Le(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
174
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Op,P0,Nl=L(()=>{Op=pP(fP),P0=64*1024*1024});function Pl(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!gP.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,gP,Dn=L(()=>{Ge=e=>!!(e&&(e.archivedAt||e.transferredTo)),gP=new Set(["merged","conflict","canceled","failed","handed-off"])});import jp from"node:fs/promises";import L0 from"node:path";import yP from"node:os";function W0(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function q0(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function K0(e){let t=new Date(W0(e));return t.setDate(t.getDate()+7),t.getTime()}function G0(e){let t=new Date(q0(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Bl(e=Date.now()){for(let[t,n]of Object.entries($s)){let r=n.startOf(e);if(r!==Bn[t]){Bn[t]=r;for(let o of go.values())o[t]=0;for(let o of Rs)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Rs.delete(o)}}}function V0(e,t){let n=Jt?.caps?.[e]?.[$s[t].capKey];return Number.isFinite(n)&&n>0?n:null}function yo(e,t=Date.now()){if(!e||!Fp||!Jt?.caps)return null;Bl(t);let n=go.get(e);for(let r of Object.keys($s)){let o=V0(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Dl(s),resetsAt:$s[r].nextStart(t)}}return null}function z0(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${Bn[t.window]}`;return Rs.has(n)?!1:(Rs.add(n),!0)}function j0(e,t,n,r=Date.now()){if(!e||!t)return;Bl(r);let o=bP(e);for(let[s,a]of Object.entries($s))n>=Bn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function kP(e,t,n=Date.now()){if(!Jt?.caps||!Lp)return;let r=go.get(e);if(r)for(let[o,s]of Object.entries($s)){let a=V0(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*H0||i>=a)continue;let c=`warn:${e}:${o}:${Bn[o]}`;if(!Rs.has(c)){Rs.add(c);try{let l=await Lp(t);if(!l)continue;await $(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Dl(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Y0(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 jl(e,{source:t="local",updatedAt:n=Date.now()}={}){Jt={caps:Y0(e),updatedAt:n,source:t};try{await jp.mkdir(F0,{recursive:!0}),await jp.writeFile(U0(),JSON.stringify(Jt,null,2))}catch{}return di()}function di(e=Date.now()){Bl(e);let t=new Set([...go.keys(),...Object.keys(Jt?.caps||{})]),n={};for(let r of t){let o=go.get(r);n[r]={weeklyUsd:Dl(o?.weekly||0),monthlyUsd:Dl(o?.monthly||0)}}return{caps:Jt?.caps||{},configured:!!Jt,seeded:Fp,updatedAt:Jt?.updatedAt??null,spent:n,windows:{weekly:{start:Bn.weekly,resetsAt:K0(e)},monthly:{start:Bn.monthly,resetsAt:G0(e)}},warnPct:H0}}async function J0({listTracks:e,getTrack:t}={}){Lp=t||null,Bl();try{let o=JSON.parse(await jp.readFile(U0(),"utf8"));Jt={caps:Y0(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{Jt=null}let n=Date.now();ct.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Bt(o);j0(o.provider||"unknown",s,o.ts??Date.now()),kP(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Bn.weekly,Bn.monthly);for(let o of await e?.()||[]){let s;try{s=await F(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||j0(a.provider||"unknown",Bt(a).usd,i,n)}}return Fp=!0,di()}var F0,U0,H0,$s,Jt,Fp,Bn,go,Rs,Lp,bP,Dl,Ll=L(()=>{de();vr();F0=process.env.BOSTRAT_STATE_DIR||L0.join(process.env.HOME||yP.tmpdir(),".bostrat"),U0=()=>L0.join(F0,"usage-caps.json"),H0=.8;$s={weekly:{startOf:W0,nextStart:K0,capKey:"weeklyUsd"},monthly:{startOf:q0,nextStart:G0,capKey:"monthlyUsd"}},Jt=null,Fp=!1,Bn={weekly:0,monthly:0},go=new Map,Rs=new Set,Lp=null,bP=e=>{let t=go.get(e);return t||(t={weekly:0,monthly:0},go.set(e,t)),t};Dl=e=>Math.round(e*1e4)/1e4});import xP from"node:fs/promises";import vP from"node:os";import X0 from"node:path";function ox(e){return e==="codex"?AP:e==="cursor"?rx:tx}function Z0(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 Q0(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 _P(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 ex(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 sx(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&&!nx.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=rx.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!EP.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function fi(e,t,{timeoutMs:n=SP}={}){let r=Date.now()+n;for(;;){let o=await Oe(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Fl(TP)}}async function IP(e,t){return await Qe(e,`/model ${t.model}`,un("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function $P(e,t){await al(e),await Qe(e,"/model",{enter:!1}),await Fl(250),await Be(e,["Enter"],{kind:"model-switch"});let n=await fi(e,Z0);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Be(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=_P(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Qe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await fi(e,i=>Q0(i)||!Z0(i)&&o(i)&&{done:o(i)});if(!s)throw await Be(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?nx.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 Be(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Qe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await fi(e,i=>!Q0(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 RP(){let e=X0.join(process.env.CURSOR_CONFIG_DIR||X0.join(vP.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await xP.readFile(e,"utf8"))?.model||null}catch{return null}}async function OP(e,t){await al(e),await cl(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await fi(e,ex),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 fi(e,ex,{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 Be(e,["Down"],{kind:"model-switch"}),await Fl(250)}if(!n||!s||!o(s)){await Be(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Be(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 Be(e,["Enter"],{kind:"model-switch"}),await Fl(800);let a=await RP();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 ax(e,t,n){return e==="codex"?$P(t,n):e==="cursor"?OP(t,n):IP(t,n)}var Fl,SP,TP,tx,EP,AP,nx,rx,ix=L(()=>{lt();Za();Fl=e=>new Promise(t=>setTimeout(t,e)),SP=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,TP=300,tx=[{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"}],EP=new Set([...tx.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"]),AP=[{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"}],nx=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),rx=[{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 Hl(e){let t=e||{},n=CP(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function CP(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:wt(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:wt(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:wt(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:wt(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:wt(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:wt(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:wt(e.agent),payload:{...Er(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:wt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Er(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:wt(e.agent),payload:{change:"subtask-resumed",...Er(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...Er(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Er(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Er(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:wt(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:wt(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:wt(e.agent),payload:{...Er(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Er(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:wt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function ux(e,t){let n=Hl(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Hp(()=>$(r,n))}async function dx(e){if(!(!e?.id||cx.has(e.id))){cx.add(e.id);try{let t=await F(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=Hl(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(e,o)}}catch{}}}function Er(e){let{type:t,at:n,...r}=e;return r}function fx(e,t,n){!n?.promptId||Up.get(e.id)===n.promptId||(Up.set(e.id,n.promptId),Hp(()=>$(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:wt(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 Wp(e,t,n,r={}){Up.delete(e.id),Hp(()=>$(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:wt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var wt,Ul,Hp,lx,cx,Up,qp=L(()=>{de();wt=e=>e||null;Ul=Promise.resolve(),Hp=e=>(Ul=Ul.then(e).catch(()=>{}),Ul),lx=()=>Ul;cx=new Set;Up=new Map});function BP(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 BP(n).reverse()){if(!MP.test(r))continue;let o=r.endsWith("?")&&r.length<=DP&&PP.test(r);if(NP.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function kx(e){let t=String(e||""),n=[];jP.test(t)&&n.push("commit"),bx.test(t)&&n.push("rebase"),LP.test(t)&&n.push("push");let r=UP.test(t);!r&&FP.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 xx(e){return bx.test(String(e||""))}function Os(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",h=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${h}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${h}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}function vx(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 Sx(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<=HP&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Tx(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+WP||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Ex(e){let t=(Array.isArray(e)?e:[]).filter(r=>px[r]).map(r=>px[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 mx,hx,wx,gx,MP,NP,PP,DP,Ar,jP,bx,LP,FP,UP,HP,WP,px,bo=L(()=>{mx=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,hx=/(?<!\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,wx=/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,gx=/(?<!\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,MP=new RegExp(`\\b(?:${mx}|${hx}|${wx}|${gx})\\b`,"i"),NP=/\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,PP=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,DP=90;Ar=["commit","rebase","push","pr"],jP=new RegExp(`\\b(?:${mx})\\b`,"i"),bx=new RegExp(`\\b(?:${gx})\\b`,"i"),LP=new RegExp(`\\b(?:${hx})\\b`,"i"),FP=new RegExp(`\\b(?:${wx})\\b`,"i"),UP=/\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;HP=3e4;WP=2500;px={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import qP from"node:crypto";import Wl from"node:fs/promises";import Ax from"node:path";import KP from"node:os";async function Fe(){return _r||(Kp??=Wl.readFile(Vp,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(_r=e,!t&&Me()){let{records:n,source:r}=await nt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;_r[oe(i.repo,i.number)]=i}Object.keys(_r).length&&await zp().catch(()=>{})}}return _r}).finally(()=>Kp=null),Kp)}async function zp(){let e=JSON.stringify({links:_r},null,2),t=`${Vp}.${process.pid}.tmp`;await Wl.mkdir(_x,{recursive:!0}),await Wl.writeFile(t,e),await Wl.rename(t,Vp)}async function et(e){Gp=Gp.then(zp,zp),await Gp.catch(()=>{});let t=_r?.[e];t&&ht("prlinks",{id:Ix(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function It({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 Fe(),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:GP.has(a),firstSeen:Date.now()},await et(oe(e,t)),{link:i[c],created:!0})}async function jn(e,t){if(!e||t==null)return null;let n=(await Fe())[oe(e,t)];return!n||n.announced?null:(n.announced=!0,await et(oe(e,t)),n)}async function rt(e,t){return!e||t==null?null:(await Fe())[oe(e,t)]??null}async function Ir(e,t,n){if(!e||t==null)return null;let r=(await Fe())[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 et(oe(e,t)),r):null}async function $x(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Fe())[oe(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await et(oe(e,t)),o):null}async function Rx(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Fe())[oe(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await et(oe(e,t)),o):null}async function ql(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Fe())[oe(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await et(oe(e,t)),r)}async function Kl(e,t){if(!e||t==null)return null;let n=(await Fe())[oe(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await et(oe(e,t)),n)}async function Gl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Fe())[oe(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await et(oe(e,t)),r)}async function Vl(e,t){if(!e||t==null)return null;let n=(await Fe())[oe(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await et(oe(e,t)),n)}async function Ox(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Fe())[oe(e,t)];return r?(r.baseFoldAt=n,await et(oe(e,t)),r):null}async function Cx(e,t){if(!e||t==null)return null;let n=(await Fe())[oe(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await et(oe(e,t)),n)}async function Mx(e,t,n){if(!e||t==null||!n)return null;let r=(await Fe())[oe(e,t)];return r?(r.lastHeadSha=n,await et(oe(e,t)),r):null}async function Nx(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Fe())[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 et(oe(e,t))),{prev:o,link:r}}async function pi(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Fe())[oe(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await et(oe(e,t)),r):null}async function ko(e,t,n=!0){if(!e||t==null)return null;let r=(await Fe())[oe(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await et(oe(e,t)),r):null}async function mi(e,t,n=!0){if(!e||t==null)return null;let r=(await Fe())[oe(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await et(oe(e,t)),r):null}async function Px(e,t,n){if(!e||t==null)return null;let r=(await Fe())[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 et(oe(e,t)),r}async function Dx(e,t,n){if(!e||t==null)return null;let r=(await Fe())[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 et(oe(e,t)),r}async function hi(e,t){if(!e||t==null)return null;let n=(await Fe())[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 et(oe(e,t)),n)}async function Bx(){return Object.values(await Fe()).filter(e=>!VP.has(e.state))}async function xo(){return Object.values(await Fe()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function jx(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await nt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Fe(),r=0;for(let[o,s]of Object.entries(n))await ht("prlinks",{id:Ix(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var _x,Vp,oe,Ix,_r,Kp,Gp,GP,VP,Ln=L(()=>{ur();_x=process.env.BOSTRAT_STATE_DIR||Ax.join(process.env.HOME||KP.tmpdir(),".bostrat"),Vp=Ax.join(_x,"pr-links.json"),oe=(e,t)=>`${e}#${Number(t)}`,Ix=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=qP.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},_r=null,Kp=null,Gp=Promise.resolve();GP=new Set(["api","observed","reconciled"]);VP=new Set(["merged","closed","lost"])});import zP from"node:fs";import Yp from"node:fs/promises";import Hx from"node:path";import YP from"node:os";function Kx(e,t=!1){if(!t&&e-Fx<JP)return;Fx=e;let n=JSON.stringify({windowStart:Cs,byCaller:Object.fromEntries(Fn),lastHour:$r});Ux=Ux.then(()=>Yp.mkdir(Wx,{recursive:!0})).then(()=>Yp.writeFile(`${zl}.tmp`,n)).then(()=>Yp.rename(`${zl}.tmp`,zl)).catch(()=>{})}function Gx(e){e-Cs<qx||($r={start:Cs,total:Yl(Fn),byCaller:Object.fromEntries(Fn)},$r.total&&console.log(`[github] ${$r.total} calls last hour`,$r.byCaller),Cs=e,Fn=new Map,Kx(e,!0))}function Un(e="other",t=Date.now()){Gx(t),Fn.set(e,(Fn.get(e)||0)+1),Kx(t)}function Vx(e=Date.now()){return Gx(e),{windowStart:Cs,total:Yl(Fn),byCaller:Object.fromEntries(Fn),lastHour:$r,backoffUntil:vo>e?vo:null}}function Rr(e){return XP.test(String(e?.message||e||""))}function Or(e,t=Date.now()){return vo<=t&&console.warn(`[github] rate limited \u2014 backing off ${Lx/6e4} min:`,String(e?.message||e||"").slice(0,200)),vo=Math.max(vo,t+Lx),vo}function Jl(e=Date.now()){return Math.max(0,vo-e)}var Wx,zl,qx,Lx,JP,Cs,Fn,$r,vo,Fx,Yl,Ux,XP,wi=L(()=>{Wx=process.env.BOSTRAT_STATE_DIR||Hx.join(process.env.HOME||YP.tmpdir(),".bostrat"),zl=Hx.join(Wx,"github-metrics.json"),qx=60*6e4,Lx=5*6e4,JP=6e4,Cs=Date.now(),Fn=new Map,$r=null,vo=0,Fx=0,Yl=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(zP.readFileSync(zl,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<qx?(Cs=e.windowStart,Fn=new Map(Object.entries(e.byCaller||{})),$r=e.lastHour||null):Yl(new Map(Object.entries(e.byCaller||{})))&&($r={start:e.windowStart,total:Yl(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Ux=Promise.resolve();XP=/rate limit|too quickly|abuse detection|HTTP 429/i});import zx from"node:fs/promises";import Yx from"node:path";import ZP from"node:os";function Zl({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};Xl.push(l),Xl.length>eD&&Xl.shift();let u=Jp.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)),Jp.set(e,u),tD(l),l}async function tD(e){try{await zx.mkdir(Jx,{recursive:!0}),await zx.appendFile(QP,JSON.stringify(e)+`
|
|
179
|
+
`)}catch{}}function Xx(){let e={};for(let[t,n]of Jp)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:Xl.slice(-20)}}var Jx,QP,eD,Xl,Jp,Ql=L(()=>{Jx=process.env.BOSTRAT_STATE_DIR||Yx.join(process.env.HOME||ZP.tmpdir(),".bostrat"),QP=Yx.join(Jx,"github-latency.ndjson"),eD=100,Xl=[],Jp=new Map});import{execFile as nD}from"node:child_process";import{promisify as rD}from"node:util";async function oD(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ns("git",["-C",e,"rev-parse","HEAD"]),Ns("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await $({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function jt(e,t,n={}){let{caller:r="prs",...o}=n,s=await sn({cwd:e});Un(r);try{let{stdout:a}=await Ns("gh",t,{cwd:e,...sD,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Rr(a)&&Or(a),a}}async function Qp(){if(eu&&Date.now()-eu.at<6e4)return eu.data;let e;try{Un("auth");let t=await Ns("gh",["auth","status"],{timeout:1e4});e={installed:!0,authenticated:!0,login:`${t.stdout||""}${t.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(t){e=t?.code==="ENOENT"||t?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(e.installed&&!e.authenticated)try{let t=await Aa();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return eu={at:Date.now(),data:e},e}async function aD(e){try{let{stdout:t}=await Ns("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Qx(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 Xp(e){let t=(e||[]).map(Qx),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 tm(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function tv(e,t){if(!t)return null;let n=await Se(e);if(!n)return null;try{let r=await jt(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 nv(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 rv(e,t){let n=await tv(e,t);return n===null?{...Xp([]),unavailable:!0}:Xp(nv(n))}function tu(e,t=Xp(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function cD(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Ms.delete(s);continue}r.add(s),Ms.has(s)||Ms.set(s,n),o.mergeabilityPending=n-Ms.get(s)<iD}for(let o of Ms.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Ms.delete(o);return t}async function Ps(e,t){let n=await Hn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await jt(n.path,["pr","view",String(r),"--json",em]);return tu(JSON.parse(o))}catch(o){if(!tm(o))throw o;let s=await jt(n.path,["pr","view",String(r),"--json",ev]),a=JSON.parse(s);return tu(a,await rv(n.path,a.headRefOid))}}async function fn(e,{caller:t="prs"}={}){try{let n=await jt(e,["pr","list","--json",em,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>tu(r)).sort(Zx)}catch(n){if(!tm(n))throw n;let r=await jt(e,["pr","list","--json",ev,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>tu(o,await rv(e,o.headRefOid))))).sort(Zx)}}function mn(){gi.clear()}async function dD(e,t){if(!e)return t;for(let n of t){let r=await rt(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 ql(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Gl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function ov(){let e=await Qp();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await Rn())await aD(a.path)&&t.push(a);let n=Date.now(),r=Jl(n)>0,o=await Promise.all(t.map(async a=>{let i=gi.get(a.path),c=i?.pending?uD:lD;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Se(a.path).catch(()=>null),u=cD(a.path,await fn(a.path,{caller:"tab"}),n);if(await dD(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&&Zl({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 gi.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:pn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of gi.keys())s.has(a)||gi.delete(a);return{auth:e,repos:o}}async function Hn(e){let t=(await Rn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function So(e){return Zo(e)}async function Ds(e,t,{method:n="squash"}={}){let r=await Hn(e);if(!yi.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 sn({cwd:r.path})).GH_TOKEN){let a=await Hd().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await jt(r.path,s)}catch(a){throw new Error(pn(a))}finally{mn()}return{ok:!0,number:o}}async function bi(e,t){let n=await Hn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await jt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(pn(s))}finally{mn()}let o=await Se(n.path).catch(()=>null);return o&&await Ox(o,r).catch(()=>{}),{ok:!0,number:r}}async function nm(e,t){let n=await Hn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await jt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(pn(o))}finally{mn()}return{ok:!0,number:r}}function rm(e){return e.green&&!e.isDraft}function hD(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 sv(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:rm(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function av(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function ki(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 iv(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(ki))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function cv(){let e={};for(let[t,n]of Zp)e[t]={...n};return e}async function lv(e,{method:t="merge",onMerged:n=null}={}){let r=await Hn(e);if(!yi.has(t))throw new Error(`invalid merge method: ${t}`);if(Zp.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await fn(r.path)).filter(rm).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 Zp.set(r.path,s),gD(s,o,{listPrs:()=>fn(r.path),merge:a=>Ds(r.path,a,{method:t}),sleep:mD,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function wD(e,t){let n=Date.now()+(t.settleMaxMs??pD),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(fD)}}async function gD(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 wD(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: ${pn(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(!rm(a)){e.skipped.push({number:a.number,title:a.title,reason:hD(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:pn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),mn()}}async function om(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ns("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${pn(l)}`)}await oD(e);let o=n||"";if((await sn({cwd:e})).GH_TOKEN){let l=await Hd().catch(()=>null);l&&(o=`${o?`${o}
|
|
180
|
+
|
|
181
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await jt(e,s)}catch(l){throw new Error(pn(l))}finally{mn()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function uv(e,t){let n=await Hn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await jt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(pn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!tm(i))throw i;let c=await jt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await tv(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(pn(i));o=nv(l).map(Qx).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=yD(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await jt(n.path,["run","view",c,"--log-failed"]).then(u=>bD(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function yD(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function bD(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
182
182
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
183
183
|
`+o.slice(-t).join(`
|
|
184
184
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
185
|
-
`+r.slice(-n)),r}function
|
|
186
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
187
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+uD(a)+"[ \\t]*$","m").exec(t.slice(i+1));if(!l)continue;let u=i+1+l.index+l[0].length;t=t.slice(0,i)+t.slice(u)}return t}function Xp(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function av(e){let t=ov(e),n=t.match(iD);if(!n)return null;let r=Xp(t,n.index);return lD.test(r)?null:{repo:r.match(sv)?.[1]??null,head:r.match(dD)?.[1]??null}}function iv(e){let t=ov(e),n=t.match(cD);if(!n)return null;let r=Xp(t,n.index),o=Xp(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("-")){pD.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(sv)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function hD(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Bn("observer");let{stdout:c}=await ev("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",mD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Tr(l)&&Ar(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-oD&&!n(c)),s=await tn({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 wD(e,t){if(t)return[t];let n=[];for(let r of cv(e)){let o=await xe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await En().catch(()=>[])){let o=await xe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function cv(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 gD(e,t,n){let r=await Os(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||fD(c.headRefName,e.id),i=[];for(let c of await wD(e,t?.repo??null)){let l;try{l=await hD(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await Qe(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function lv(e,t,{delays:n=tv,turnEnd:r}={}){let o=av(t.command);if(!o)return null;let s=!!iv(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Er.has(a))return null;Er.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await gD(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:m}=await St({repo:d,number:f.number,trackId:e.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(p?.trackId===e.id&&await Nn(d,f.number))?null:(await Yp(t.box,t.agent,r),await O({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{Er.delete(a)}})();return c.catch(()=>Er.delete(a)),c}function uv(e,t,{delays:n=tv,turnEnd:r,openedWait:o}={}){let s=iv(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Er.has(a))return null;Er.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let d=[];if(c)d=[c];else{let m=cv(e);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await xe(w).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await Os(e).catch(()=>[])).filter(m=>m!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(m,w)=>{let y=await Qe(c,m.number);return y&&y.trackId!==e.id?null:(y||await St({repo:c,number:m.number,trackId:e.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await ci(c,m.number),w&&((await F(e).catch(()=>[])).some(I=>I.type==="pr.state.changed"&&(I.payload?.change==="auto-armed"||I.payload?.change==="merged")&&I.payload?.number===m.number)||(await Yp(t.box,t.agent,r),await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(S=>setTimeout(S,m));let w=[];for(let S of d)try{Bn("observer");let{stdout:A}=await ev("gh",["pr","view",String(l),"--repo",S,"--json",yD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await tn({slug:S})}}),R=JSON.parse(A||"null");R?.number&&w.push({slug:S,pr:R})}catch(A){Tr(A)&&Ar(A)}let y=w.find(S=>S.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let x=y.pr;if(!x?.number)continue;if(x.state==="CLOSED")return null;if(x.state!=="MERGED"){if(p=x,s.auto)return f(x,!0);continue}let T=await Qe(c,x.number);if(T&&T.trackId!==e.id)return null;T||await St({repo:c,number:x.number,trackId:e.id,url:x.url,headRef:x.headRefName,title:x.title,source:"merge-observed"}),await Yp(t.box,t.agent,r);let I=await F(e).catch(()=>[]);if(I.some(S=>S.type==="pr.state.changed"&&S.payload?.change==="merged"&&S.payload?.number===x.number))return null;let $=S=>S.some(A=>A.type==="pr.state.changed"&&A.payload?.change==="opened"&&A.payload?.number===x.number),D=0;if(!$(I)){if(av(t.command)&&await Nn(c,x.number)){let S=Date.now();D=S+1,await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:S,change:"opened",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c,selfMerge:!0}})}else if((await Qe(c,x.number))?.announced){let S=o?.capMs??sD,A=o?.pollMs??aD,R=Date.now()+S;for(;Date.now()<R&&(await new Promise(E=>setTimeout(E,A)),!$(await F(e).catch(()=>[]))););}}return await O({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(),D),change:"merged",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c}}),await Fn(c,x.number).catch(()=>{}),await Ln(c,x.number).catch(()=>{}),await vr(c,x.number,"merged"),x}return p?f(p,!1):null}finally{Er.delete(a)}})();return i.catch(()=>Er.delete(a)),i}var ev,tv,nD,rD,oD,sD,aD,nv,rv,iD,cD,lD,Qx,uD,sv,dD,fD,pD,mD,Er,yD,dv=j(()=>{ue();tr();it();di();Pn();_r();gi();Cs();ev=tD(eD),tv=[4e3,15e3,45e3],nD=2500,rD=5*6e4;oD=15e3,sD=9e4,aD=2e3,nv=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,rv=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,iD=new RegExp(`${nv}${rv}create\\b`),cD=new RegExp(`${nv}${rv}merge\\b`),lD=/\s--web\b/,Qx=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,uD=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");sv=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,dD=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,fD=Zx;pD=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);mD="number,url,title,headRefName,createdAt";Er=new Set;yD="number,url,title,headRefName,state"});import bD from"node:fs/promises";import Qp from"node:path";import kD from"node:os";function Zp(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Zl(e){let t=xD.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Zp(n,"from"),s=o?o.replace(/^uds:/,""):null,a=fv(s);return{socket:s,pid:a,name:Zp(n,"from-name"),mode:Zp(n,"from-mode"),text:r.trim()}}function fv(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function vD(){return process.env.CLAUDE_CONFIG_DIR?Qp.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Qp.join(kD.homedir(),".claude","sessions")}async function SD(e){let t=fv(e);if(!t)return null;try{let n=await bD.readFile(Qp.join(vD(),`${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 pv(e,t){let n=await SD(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 xD,em=j(()=>{xD=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function tm(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function nm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!tm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),mv);try{let a=await hv(`${$e(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 wv(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(!tm(s))return{ok:!1,source:"unconfigured"};let i=Ql.get(e.id)||0;if(a()-i<TD)return{ok:!1,source:"debounced"};Ql.set(e.id,a());let c=await yi({env:s}),l=c.loops.find(f=>f.trackId===e.id);if(!l)return Ql.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 nm({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||Ql.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function yi({env:e=process.env}={}){if(!tm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),mv);try{let r=await hv(`${$e(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 mv,hv,TD,Ql,rm=j(()=>{it();mv=1e4,hv=(...e)=>fetch(...e);TD=10*6e4,Ql=new Map});import{randomUUID as AD}from"node:crypto";function bi(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function gv(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function yv(e){return`job|${e.box}|id:${e.id}`}async function sm(e){return om.has(e.id)?om.get(e.id):(eu.has(e.id)||eu.set(e.id,F(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(bi({role:o,sourceId:r.payload.sourceId})),n.keys.add(bi({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(bi({role:"user",sourceId:r.payload.sourceId})),n.keys.add(bi({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(gv({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(yv({id:r.payload.id,box:r.boxId}));return om.set(e.id,n),n}).finally(()=>eu.delete(e.id))),eu.get(e.id))}async function bv(e,t){let n=new Set;try{let r=(await sm(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=bi(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?Zl(o.text):null;if(a){await O(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 pv(a,ki),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=eb(o);if(i){await O(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}}),wv(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 O(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=tx(o.text);if(c){let l=ox(c),u;if(e.workspace?.kind==="multi"){let m=await Ms(e).catch(()=>[]);u=m.length?m.map(w=>({name:w.name,slug:w.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await Rs(e.cwd).catch(()=>null))||e.workspace.branch,f=await Vx(e,{branch:d,rebase:l}).catch(()=>null),p=f?null:rx(c);if(f&&f.available.length===0)continue;await O(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:AD(),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 kv(e,t){try{let n=await sm(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=gv(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await O(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}}),lv(e,o),uv(e,o))}}catch{}}async function xv(e,t){try{let n=await sm(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=yv(o);n.keys.has(s)||(n.keys.add(s),await O(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 om,eu,vv=j(()=>{ue();Ss();zx();dv();Cs();em();_r();rl();rm();om=new Map,eu=new Map});function ED(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
`)
|
|
191
|
-
`)){
|
|
185
|
+
`+r.slice(-n)),r}function pn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
186
|
+
`).find(n=>n.trim())||"gh failed"}var Ns,sD,eu,em,ev,iD,Ms,Zx,lD,uD,gi,yi,fD,pD,mD,Zp,Bs=L(()=>{ar();it();Ln();wi();Ql();de();Ns=rD(nD),sD={timeout:3e4,maxBuffer:8*1024*1024};eu=null;em="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",ev=em.replace("statusCheckRollup,","");iD=9e4,Ms=new Map;Zx=(e,t)=>e.number-t.number;lD=3e4,uD=1500,gi=new Map;yi=new Set(["squash","merge","rebase"]);fD=2500,pD=9e4,mD=e=>new Promise(t=>setTimeout(t,e));Zp=new Map});import{execFile as kD}from"node:child_process";import{promisify as xD}from"node:util";async function SD(e,t){try{let{stdout:n}=await vD("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function TD(e,t,n,{rebase:r=!1,listPrs:o=fn}={}){let s=await li(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await ui(e.dir,n).catch(()=>0),i=t?await SD(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let f=s||a>0,d=[];s&&d.push("commit"),r&&d.push("rebase"),(s||c)&&d.push("push"),f&&(u!=="open"||s||c)&&d.push("pr");let p=d.filter(h=>!(h==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:nu.filter(h=>d.includes(h)),defaults:nu.filter(h=>p.includes(h))}}async function ru(e,{branch:t,rebase:n=!1,listPrs:r=fn}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(d=>d?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await Es(s[0].dir).catch(()=>!1))return null;let a=[];for(let d of s)a.push(await TD(d,t,d.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(d=>d.dirty||d.aheadBase>0||d.openPr),c=i.length?i:a,l=nu.filter(d=>c.some(p=>p.available.includes(d))),u=nu.filter(d=>c.some(p=>p.defaults.includes(d))),f=c.filter(d=>d.openPr);return{available:l,defaults:u,prState:f.length?"open":c.every(d=>d.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(d=>({name:d.name,slug:d.slug,dirty:d.dirty,unpushed:d.unpushed,prState:d.prState,openPr:d.openPr}))}}var vD,nu,sm=L(()=>{Nl();Bs();vD=xD(kD),nu=["commit","rebase","push","pr"]});function ou(e,t,n){let{state:r,reason:o}=Nf(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of xi)a-c.at>=dv()&&xi.delete(i);xi.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=xi.get(e);return s?Date.now()-s.at>=dv()?(xi.delete(e),!1):s.busy:!1}async function am(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Oe(e,{lines:Ze}).catch(()=>"");if(!ou(e,t,r))return Db(t,r)?{working:!0,prompted:!1}:n;if(wo(t,r)){let o=await Mn(e).catch(()=>r),s=Tr({name:e,agent:t},o);if(s)return!Cl({name:e,agent:t},s,o)||M0(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function To(e,t){return(await am(e,t)).working}var dv,xi,vi=L(()=>{lt();Xa();_p();$p();dv=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,xi=new Map});import{execFile as ED}from"node:child_process";import{promisify as AD}from"node:util";async function Cr(e){if(!e)return null;let t=fv.get(e);if(t&&Date.now()-t.at<ID)return t.branch;let n=null;try{let{stdout:r}=await _D("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return fv.set(e,{at:Date.now(),branch:n}),n}function pv(e){return String(e||"").match($D)?.[1]??null}function RD(e,t){let n=pv(e);return!!n&&!!t&&n===t}function mv(e,t){let n=pv(e);return!!n&&n!==t}function OD(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 js(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of OD(e))n(await Cr(r));return t}async function su(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=>RD(t,a.id));if(s)return s;for(let a of r)if(!Ge(a)&&(await js(a)).includes(t))return a;return null}var _D,ID,fv,$D,Eo=L(()=>{Dn();_D=AD(ED),ID=5e3,fv=new Map;$D=/^track\/.+-([0-9a-f]{8})$/});import{execFile as CD}from"node:child_process";import{promisify as MD}from"node:util";async function im(e,t,{midTurn:n=To,pollMs:r=ND,capMs:o=PD}={}){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 kv(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){hv.lastIndex=n;let o=hv.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(`
|
|
187
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+HD(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 cm(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function vv(e){let t=kv(e),n=t.match(LD);if(!n)return null;let r=cm(t,n.index);return UD.test(r)?null:{repo:r.match(xv)?.[1]??null,head:r.match(WD)?.[1]??null}}function Sv(e){let t=kv(e),n=t.match(FD);if(!n)return null;let r=cm(t,n.index),o=cm(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("-")){KD.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(xv)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function VD(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Un("observer");let{stdout:c}=await wv("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",GD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Rr(l)&&Or(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-DD&&!n(c)),s=await sn({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 zD(e,t){if(t)return[t];let n=[];for(let r of Tv(e)){let o=await Se(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Rn().catch(()=>[])){let o=await Se(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Tv(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 YD(e,t,n){let r=await js(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||qD(c.headRefName,e.id),i=[];for(let c of await zD(e,t?.repo??null)){let l;try{l=await VD(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await rt(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 Ev(e,t,{delays:n=gv,turnEnd:r}={}){let o=vv(t.command);if(!o)return null;let s=!!Sv(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Mr.has(a))return null;Mr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await YD(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:h}=await It({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 jn(f,d.number))?null:(await im(t.box,t.agent,r),await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{Mr.delete(a)}})();return c.catch(()=>Mr.delete(a)),c}function Av(e,t,{delays:n=gv,turnEnd:r,openedWait:o}={}){let s=Sv(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Mr.has(a))return null;Mr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let h=Tv(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let g=await Se(w).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=(await js(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(h,w)=>{let g=await rt(c,h.number);return g&&g.trackId!==e.id?null:(g||await It({repo:c,number:h.number,trackId:e.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await pi(c,h.number),w&&((await F(e).catch(()=>[])).some(A=>A.type==="pr.state.changed"&&(A.payload?.change==="auto-armed"||A.payload?.change==="merged")&&A.payload?.number===h.number)||(await im(t.box,t.agent,r),await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},p=null;for(let h of n){await new Promise(T=>setTimeout(T,h));let w=[];for(let T of f)try{Un("observer");let{stdout:_}=await wv("gh",["pr","view",String(l),"--repo",T,"--json",JD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await sn({slug:T})}}),O=JSON.parse(_||"null");O?.number&&w.push({slug:T,pr:O})}catch(_){Rr(_)&&Or(_)}let g=w.find(T=>T.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!g)continue;c=g.slug;let k=g.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return d(k,!0);continue}let S=await rt(c,k.number);if(S&&S.trackId!==e.id)return null;S||await It({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await im(t.box,t.agent,r);let A=await F(e).catch(()=>[]);if(A.some(T=>T.type==="pr.state.changed"&&T.payload?.change==="merged"&&T.payload?.number===k.number))return null;let R=T=>T.some(_=>_.type==="pr.state.changed"&&_.payload?.change==="opened"&&_.payload?.number===k.number),B=0;if(!R(A)){if(vv(t.command)&&await jn(c,k.number)){let T=Date.now();B=T+1,await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:T,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await rt(c,k.number))?.announced){let T=o?.capMs??BD,_=o?.pollMs??jD,O=Date.now()+T;for(;Date.now()<O&&(await new Promise(I=>setTimeout(I,_)),!R(await F(e).catch(()=>[]))););}}return await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),B),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await qn(c,k.number).catch(()=>{}),await Wn(c,k.number).catch(()=>{}),await Ir(c,k.number,"merged"),k}return p?d(p,!1):null}finally{Mr.delete(a)}})();return i.catch(()=>Mr.delete(a)),i}var wv,gv,ND,PD,DD,BD,jD,yv,bv,LD,FD,UD,hv,HD,xv,WD,qD,KD,GD,Mr,JD,_v=L(()=>{de();ar();it();wi();Ln();Nr();vi();Eo();wv=MD(CD),gv=[4e3,15e3,45e3],ND=2500,PD=5*6e4;DD=15e3,BD=9e4,jD=2e3,yv=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,bv=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,LD=new RegExp(`${yv}${bv}create\\b`),FD=new RegExp(`${yv}${bv}merge\\b`),UD=/\s--web\b/,hv=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,HD=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");xv=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,WD=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,qD=mv;KD=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);GD="number,url,title,headRefName,createdAt";Mr=new Set;JD="number,url,title,headRefName,state"});import XD from"node:fs/promises";import um from"node:path";import ZD from"node:os";function lm(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function au(e){let t=QD.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=lm(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Iv(s);return{socket:s,pid:a,name:lm(n,"from-name"),mode:lm(n,"from-mode"),text:r.trim()}}function Iv(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function eB(){return process.env.CLAUDE_CONFIG_DIR?um.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):um.join(ZD.homedir(),".claude","sessions")}async function tB(e){let t=Iv(e);if(!t)return null;try{let n=await XD.readFile(um.join(eB(),`${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 $v(e,t){let n=await tB(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 QD,dm=L(()=>{QD=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function fm(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function pm({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(),Rv);try{let a=await Ov(`${_e(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 Cv(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=iu.get(e.id)||0;if(a()-i<nB)return{ok:!1,source:"debounced"};iu.set(e.id,a());let c=await Si({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return iu.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 pm({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||iu.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function Si({env:e=process.env}={}){if(!fm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),Rv);try{let r=await Ov(`${_e(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 Rv,Ov,nB,iu,mm=L(()=>{it();Rv=1e4,Ov=(...e)=>fetch(...e);nB=10*6e4,iu=new Map});function Mv(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Nv(e,t,n=hm){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await oB(`${_e(t)}${e}`,{headers:sB(t),signal:r.signal})}finally{clearTimeout(o)}}async function Ti({env:e=process.env,force:t=!1,timeoutMs:n=hm}={}){if(!Mv(e))return{followups:[],source:"unconfigured"};if(!t&&Ao&&Date.now()-Ao.at<rB)return{followups:Ao.followups,source:"cache"};try{let r=await Nv("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return Ao={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Ao?{followups:Ao.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function Pv(e,{env:t=process.env,timeoutMs:n=hm}={}){if(!e)return{followup:null,source:"unconfigured"};if(!Mv(t))return{followup:null,source:"unconfigured"};try{let r=await Nv(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=Ao?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var rB,hm,Ao,oB,sB,Dv=L(()=>{it();rB=6e4,hm=5e3,Ao=null,oB=(...e)=>fetch(...e);sB=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import{randomUUID as Fs,createHash as aB}from"node:crypto";function Fv(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function dB(e){for(let[t,n]of uB)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 Uv(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Ls(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>Bv)throw new Error(`label must be \u2264${Bv} characters`);let o=Ls(n.trigger)||(n.ship?"now":""),s=t?cB:Lv.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"?pB(n.ship):null,i=Ls(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>jv)throw new Error(`prompt must be \u2264${jv} bytes`);if(t){let u=dB(i);if(u)throw new Error(u)}let c=Ls(n.why).slice(0,iB)||null,l=t?fB(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function fB(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 pB(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):wm.slice();for(let i of n)if(!wm.includes(i))throw new Error(`ship.actions may only contain ${wm.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=Ls(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>Ls(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function cu(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 Hv(e,t,{pull:n=Ti,ship:r={}}={}){if(bm(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=Uv(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=Fs(),k=Fs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:g,trigger:"now",label:o.label,prompt:o.prompt,why:o.why}}),await $(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:k,trigger:"now",repo:e.workspace?.repo||null,context:{},rows:[{armId:g,label:o.label,prompt:o.prompt,promptHash:gm(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=Fv(o.label),f=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(f)return{kind:"covered",covered:{id:f.id,label:f.label},...o};let d=await F(s),p=cu(d,{trigger:o.trigger}),h=p.length?p[p.length-1].armId:null,w=Fs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:w,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...h?{replaces:h}:{}}}),{kind:"armed",armId:w,replaces:h,...o}}async function Wv(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await F(n);if(!cu(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 qv(e,t,{pull:n=Ti}={}){if(bm(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=Uv(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await F(s)).reduce((g,k)=>k.type==="followup.create.offered"?k.payload?.proposalId??g:k.type==="followup.create.answered"&&k.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=Fv(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}" (${ym[p.trigger]||p.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(d){let g=d.deletedAt?new Date(d.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${d.label}" on ${g} \u2014 do not re-propose it`)}let h=Fs(),w=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await $(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:w?.agent??null,payload:{ts:Date.now(),proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function Kv(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await F(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await $(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Ei(e,t,n={},{pull:r=Ti}={}){if(!e||bm(e))return null;let o={id:e.id,cwd:e.cwd},s=await F(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null,i=cu(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:mB(p.prompt),promptHash:gm(p.prompt),origin:p.origin,repo:p.repo??null}))}let l=[...i,...c].slice(0,lB);if(!l.length)return null;let u=Fs(),f={ts:Date.now(),offerId:u,trigger:t,repo:a,context:{...n,repo:a},rows:l};return await $(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:f}),f}function 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 Gv(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=Pv}=s;if(!t)throw new Error("offerId required");let l={id:e.id,cwd:e.cwd},u=await F(l),f=u.find(A=>A.type==="followup.offered"&&A.payload?.offerId===t);if(!f)throw new Error("unknown offer");if(u.some(A=>A.type==="followup.answered"&&A.payload?.offerId===t))throw new Error("offer already answered");let d=f.payload.rows||[],p=A=>$(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...A}});if(!n&&!r)return await p({action:"dismiss",followupIds:d.map(A=>A.followupId).filter(Boolean),armIds:d.map(A=>A.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let h=null,w=null;if(n){if(!d.some(B=>B.followupId===n))throw new Error("that follow-up is not on this card");let{followup:A,source:R}=await c(n);if(R==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!A||A.status!=="active"){await p({action:"removed",followupId:n});let B=new Error("removed in Settings since it was offered");throw B.code="removed",B}h=A.prompt,w=A.label}else{let A=d.find(B=>B.armId===r);if(!A)throw new Error("that arm is not on this card");if(h=u.find(B=>B.type==="followup.armed"&&B.payload?.armId===r)?.payload?.prompt??A.prompt,w=A.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=`${hB(f.payload)}
|
|
188
|
+
|
|
189
|
+
${h}`,S=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:!!S?.queued}}async function wB(e,t,{changedRepos:n=null,probe:r=ru,branchOf:o=Cr}={}){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(S=>s.repos.includes(S.name)):g;l=k.length?k.map(S=>({name:S.name,slug:S.repo??null})):void 0}let u=(c?null:await o(e.cwd).catch(()=>null))||e.workspace.branch,f=s.rebase==="offered",d=await r(e,{branch:u,rebase:f}).catch(()=>null),p=[...f?["rebase"]:[],...s.actions].filter(g=>Ar.includes(g));if(d&&d.available.length===0)return{skipped:!0,reason:"nothing to ship \u2014 the tree is clean, pushed, and its PR is open",shipState:d};let h=Fs(),w=t.why?`${t.label} \u2014 ${t.why}`:t.label;return await $(a,{type:"pr.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:i?.agent??null,payload:{offerId:h,ts:Date.now(),excerpt:w,via:"cli",branch:u,base:s.base||e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:"lead",available:d?.available,defaults:d?d.defaults.filter(g=>p.includes(g)||g==="rebase"):void 0,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||l,offered:d?p.filter(g=>d.available.includes(g)):p,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:h,branch:u,offered:p,available:d?.available??null,prState:d?.prState??null}}function Vv(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 zv(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 Yv(e,{pull:t=Ti}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await F({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?cu(s):[]}}var Bv,jv,iB,Lv,cB,ym,lB,wm,gm,Ls,uB,bm,mB,lu=L(()=>{de();bo();sm();Eo();Dv();Bv=60,jv=8*1024,iB=200,Lv=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],cB=Lv.filter(e=>e!=="now"&&e!=="track.opened"),ym={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"},lB=3,wm=["commit","push","pr"];gm=e=>aB("sha256").update(String(e)).digest("hex").slice(0,16),Ls=e=>typeof e=="string"?e.trim():"",uB=[[/(^|[^\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"]];bm=e=>String(e?.name||"").startsWith("\u21BB");mB=e=>String(e||"").split(`
|
|
190
|
+
`).slice(0,3).join(`
|
|
191
|
+
`).slice(0,400)});import{randomUUID as gB}from"node:crypto";function Ai(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function Jv(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function Xv(e){return`job|${e.box}|id:${e.id}`}async function xm(e){return km.has(e.id)?km.get(e.id):(uu.has(e.id)||uu.set(e.id,F(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(Ai({role:o,sourceId:r.payload.sourceId})),n.keys.add(Ai({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(Ai({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Ai({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(Jv({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(Xv({id:r.payload.id,box:r.boxId}));return km.set(e.id,n),n}).finally(()=>uu.delete(e.id))),uu.get(e.id))}async function Zv(e,t){let n=new Set;try{let r=(await xm(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=Ai(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?au(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 $v(a,_i),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=gb(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}}),Cv(e,{provider:o.agent||null,cls:i.cls,status:i.status,text:o.text}).catch(()=>{});continue}if(o.role!=="user"&&o.box&&n.add(o.box),await $(e,{type:o.role==="user"?"turn.user":"turn.agent",actor:o.role==="user"?"user":"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:o.text,ts:o.ts,model:o.model||null,tools:o.tools||void 0,boxRole:o.boxRole,sourceId:o.sourceId||void 0,note:o.note||void 0,images:o.images?.length?o.images:void 0}}),o.role==="assistant"&&o.box===e.leadBox&&e.workspace?.branch){let c=yx(o.text);if(c&&Vv(await F(e).catch(()=>[])))continue;if(c){let l=xx(c),u;if(e.workspace?.kind==="multi"){let h=await _o(e).catch(()=>[]);u=h.length?h.map(w=>({name:w.name,slug:w.repo??null})):void 0}let f=(e.workspace?.kind==="multi"?null:await Cr(e.cwd).catch(()=>null))||e.workspace.branch,d=await ru(e,{branch:f,rebase:l}).catch(()=>null),p=d?null:kx(c);if(d&&d.available.length===0)continue;await $(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:gB(),ts:o.ts,excerpt:c,branch:f,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:d?.available,defaults:d?.defaults,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||u,offered:d?.defaults||p?.offered||void 0,toPr:p?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function Qv(e,t){try{let n=await xm(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=Jv(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}}),Ev(e,o),Av(e,o))}}catch{}}async function eS(e,t){try{let n=await xm(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Xv(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 km,uu,tS=L(()=>{de();bo();sm();_v();Eo();dm();Nr();ul();mm();lu();km=new Map,uu=new Map});function yB(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
192
|
+
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;t.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return t}async function bB(e,t){let n=`${e.id}:${t.name}`;if(!vm.has(n)){let r=await F(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);vm.set(n,o)}return vm.get(n)}async function nS(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await so(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await bB(e,t),o=yB(await St(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:dn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await $(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var vm,rS=L(()=>{de();io();zt();Pn();vm=new Map});import du from"node:fs";import Tm from"node:path";import kB from"node:os";async function SB({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(vB,{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:oS})});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<oS)break}return r}function TB(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 aS(){try{return JSON.parse(du.readFileSync(fu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function AB(e){try{du.mkdirSync(Tm.dirname(fu()),{recursive:!0});let t=fu()+".tmp";du.writeFileSync(t,JSON.stringify(e)),du.renameSync(t,fu())}catch{}}async function IB({fetchImpl:e,now:t=Date.now()}={}){if(Pr.rows&&t-Pr.at<EB)return Pr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=aS(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await SB({sinceMs:o,cookie:n,fetchImpl:e});Pr={at:t,rows:s.map(TB).filter(Boolean),error:null}}catch(s){Pr={at:t,rows:null,error:String(s?.message||s)}}return Pr.rows}async function $B(e,t){let n=`${e.id}:${t.name}`;if(!Sm.has(n)){let r=await F(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);Sm.set(n,o)}return Sm.get(n)}async function iS(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!sS())return;let r=await IB({fetchImpl:n});if(!r||!r.length)return;let o=await d0(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await $B(e,t),a=aS(),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=dn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await $(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}AB({sinceMs:c,seen:[...i].slice(-_B)})}catch{}}var xB,sS,vB,oS,EB,Pr,fu,_B,Em,Sm,Am=L(()=>{de();pp();Pn();xB=process.env.BOSTRAT_STATE_DIR||Tm.join(process.env.HOME||kB.tmpdir(),".bostrat"),sS=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,vB="https://cursor.com/api/dashboard/get-filtered-usage-events",oS=100;EB=5*6e4,Pr={at:0,rows:null,error:null},fu=()=>Tm.join(xB,"cursor-usage.json");_B=5e3;Em=()=>({enabled:sS(),lastPollAt:Pr.at||null,error:Pr.error}),Sm=new Map});import{execFile as RB}from"node:child_process";import{promisify as OB}from"node:util";function Us({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function pu(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function mu({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?DB[r]:null;return a&&s.push(a),s.push(`${dS}: ${Us({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${fS}: ${String(o).trim()}`),s}function cS(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
193
|
+
`)){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===dS.toLowerCase()&&!t.epoch)t.epoch=pu(a);else if(s===fS.toLowerCase()&&!t.model)t.model=a;else if(s===pS.toLowerCase()&&!t.cause){let i=a.toLowerCase();Ii.includes(i)&&(t.cause=i)}else if(s==="co-authored-by"||s==="made-with"){let i=a.toLowerCase();t.provider||(i.startsWith("claude")?t.provider="claude":i.startsWith("codex")?t.provider="codex":i.startsWith("cursor")&&(t.provider="cursor"))}}return t}function BB(e,t=Dr){return e<=t?"rework":"drift"}function jB(e,{window:t=Dr,end:n=Rm}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function HB({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return LB.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${pS} trailer`}:UB.test(s)&&!lS.test(s)?{cause:"style",level:"C",basis:"subject"}:lS.test(s)?{cause:"defect",level:"C",basis:"subject"}:FB.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 qB(e){return WB.some(t=>t.test(e))}function KB(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
194
|
+
`)){if(r.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1},t.push(n);continue}if(n){if(r.startsWith("--- ")){let o=r.slice(4).trim();n.oldPath=o==="/dev/null"?null:o.replace(/^a\//,"")}else if(r.startsWith("+++ ")){let o=r.slice(4).trim();n.newPath=o==="/dev/null"?null:o.replace(/^b\//,"")}else if(r.startsWith("Binary files"))n.binary=!0;else if(r.startsWith("@@")){let o=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(r);if(!o)continue;let s=Number(o[1]),a=o[2]==null?1:Number(o[2]),i=o[4]==null?1:Number(o[4]);a>0&&n.deleted.push([s,a]),n.added+=i}}}return t}function GB(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
195
|
+
`)){let o=/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(r);if(o){n={sha:o[1],line:Number(o[3]),time:null},t.set(n.line,n);continue}n&&r.startsWith("committer-time ")&&(n.time=Number(r.slice(15))*1e3)}return t}async function uS(e,t){let{stdout:n}=await $m(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
192
196
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
193
|
-
`)}}async function Dv(e,t,{fixKey:n=null,window:r=$r,resolveOrigin:o=null,context:s={}}={}){let a=await Ov(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=$v(a.message),l=n?ru(n):c.epoch,u=l?Ns(l):null,{stdout:d}=await pm(e,["diff","-U0","--no-color","-M","-C",i,t]),f=QD(d),p=0,m=0,w=0,y=new Map;for(let D of f){if(D.binary)continue;let S=D.oldPath||D.newPath||"",A=D.deleted.reduce((E,[,L])=>E+L,0);if(ZD(S)){w+=A;continue}if(p+=D.added,m+=A,!D.oldPath||!A)continue;let R;try{let E=["blame","-w","-M","-C","--line-porcelain"];for(let[L,U]of D.deleted)E.push("-L",`${L},${L+U-1}`);E.push(i,"--",D.oldPath),R=e1((await pm(e,E)).stdout)}catch{continue}for(let{sha:E,time:L}of R.values()){let U=y.get(E)??{lines:0,time:L};U.lines+=1,y.set(E,U)}}let x=[],T=0,I=new Map;for(let[D,{lines:S,time:A}]of y){let R=I.get(D);if(!R){try{R=await Ov(e,D)}catch{R=null}I.set(D,R)}let E=R?$v(R.message):{epoch:null},L=E.epoch,U=E.provider,se=E.model;if(!L&&o){let te=await o(D,R);te?.trackId&&te?.boxId!=null&&(L={trackId:te.trackId,boxId:te.boxId,epoch:te.epoch??0},U=U??te.provider??null,se=se??te.model??null)}if(!L){T+=S;continue}let G=Number.isFinite(A)?A:R?.ts??a.ts,C=Math.max(0,(a.ts-G)/HD),Z=Ns(L),Y=Z===u?"self":KD(C,r);x.push({sha:D,key:Z,trackId:L.trackId,boxId:L.boxId,epoch:L.epoch,provider:U??null,model:se??null,lines:S,ageDays:Math.round(C*100)/100,class:Y,decay:Y==="rework"?1:Y==="drift"?Math.round(GD(C,{window:r})*1e3)/1e3:0})}x.sort((D,S)=>S.lines-D.lines);let $=YD({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:p,deleted:m,excludedDeleted:w,origins:x,unattributedLines:T,ghost:m===0&&p>0,heuristic:$}}function Bv(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function fm(e){let t=e.genUsd;return{...e,genUsd:Vt(t),reworkChargedUsd:Vt(e.reworkChargedUsd),reworkPendingUsd:Vt(e.reworkPendingUsd),trueUsd:Vt(t+e.reworkChargedUsd),multiplier:t>0?Vt((t+e.reworkChargedUsd)/t):null}}function jv(e,{window:t=$r,boxMeta:n={}}={}){let r=e||[],o=r.filter(S=>S.type==="cost.recorded"),s=r.filter(S=>S.type==="code.committed"),a=r.filter(S=>S.type==="code.rework"),i=new Map;for(let S of r)S.type==="rework.cause.answered"&&S.payload?.sha&&S.payload?.cause&&i.set(S.payload.sha,S.payload.cause);let c=new Set;for(let S of r)S.type==="rework.cause.offered"&&S.payload?.sha&&c.add(S.payload.sha);let l={},u=(S,A,R)=>Ns({trackId:S,boxId:A,epoch:R??0}),d=(S,A={})=>{if(!l[S]){let E=ru(S)||{};l[S]={key:S,...E,provider:null,model:null,...dm()}}let R=l[S];return A.provider&&!R.provider&&(R.provider=A.provider),A.model&&!R.model&&(R.model=A.model),R};for(let S of o){if(!S.boxId)continue;let A=u(S.trackId,S.boxId,S.epoch),{usd:R,model:E}=Ot(S),L=d(A,{provider:S.provider,model:E!=="unknown"?E:null});L.genUsd+=R}let f=new Map;for(let S of s){let A=`${S.trackId}/${S.boxId}`;(f.get(A)??f.set(A,[]).get(A)).push(S)}for(let S of f.values())S.sort((A,R)=>A.ts-R.ts);let p=(S,A,R,E)=>{let L=f.get(`${S}/${A}`)||[],U=-1/0;for(let C of L)C.ts<E&&(U=C.ts);let se=0,G=0;for(let C of o)C.trackId!==S||C.boxId!==A||(C.epoch??0)===(R??0)&&(C.ts<=U||C.ts>E||(se+=Ot(C).usd,G++));return{usd:se,turns:G,from:Number.isFinite(U)?U:null}},m={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0},w={A:0,B:0,C:0,E:0,none:0},y=[],x=[],T={},I={},$=new Set;for(let S of a){let A=S.payload||{};if(!A.sha||$.has(A.sha))continue;$.add(A.sha);let R=ru(A.fixKey)||{trackId:S.trackId,boxId:S.boxId,epoch:S.epoch??0},{usd:E,turns:L}=p(R.trackId,R.boxId,R.epoch,A.ts??S.ts),U=null;i.has(A.sha)?U={cause:i.get(A.sha),level:"B",basis:"operator"}:(A.heuristic?.level&&A.heuristic.level!=="C"||A.heuristic)&&(U=A.heuristic);let se=U?.level??"none";w[se]=(w[se]||0)+1;let G=WD.has(se),C=Number(A.deleted)||0,Z=Array.isArray(A.origins)?A.origins:[],Y=Z.filter(we=>we.class==="rework").reduce((we,ce)=>we+ce.lines,0),te={sha:A.sha,subject:A.subject??"",ts:A.ts??S.ts,trackId:S.trackId,fixKey:A.fixKey??null,fixProvider:A.fixProvider??S.provider??null,fixUsd:Vt(E),fixTurns:L,linesReworked:Y,unattributedLines:Number(A.unattributedLines)||0,ghost:A.ghost===!0,verdict:U?{cause:U.cause,level:se,basis:U.basis??null}:null,charged:Vt(0),origins:[]};if(A.ghost||C===0)m.ghost+=E;else{let we=0;for(let V of Z){let ge=V.lines/C,B=E*ge;if(we+=B,V.class==="self"){m.self+=B;continue}let ye=V.class==="rework"?1:Number(V.decay)||0,be=B*ye;if(m.decayed+=B-be,be<=0)continue;let _e=d(V.key,{provider:V.provider,model:V.model});_e.fixes+=1,_e.linesReworked+=V.class==="rework"?V.lines:0;let Ie=U?.cause&&xi.includes(U.cause)?U.cause:"unclassified";_e.causes[Ie]+=1,G?(_e.reworkChargedUsd+=be,m.charged+=be,te.charged=Vt(te.charged+be)):(_e.reworkPendingUsd+=be,m.pending+=be),te.origins.push({key:V.key,provider:_e.provider,model:_e.model,lines:V.lines,ageDays:V.ageDays,class:V.class,usd:Vt(be)})}let ce=Math.max(0,E-we);m.unattributed+=ce}y.push(te),!i.has(A.sha)&&c.has(A.sha)&&x.push(A.sha)}for(let S of Object.values(l)){let A=S.provider||"unknown",R=S.model||"unknown",E=L=>{L.genUsd+=S.genUsd,L.reworkChargedUsd+=S.reworkChargedUsd,L.reworkPendingUsd+=S.reworkPendingUsd,L.linesReworked+=S.linesReworked,L.fixes+=S.fixes;for(let U of Object.keys(L.causes))L.causes[U]+=S.causes[U]};E(T[A]||=dm()),E(I[R]||=dm())}y.sort((S,A)=>A.ts-S.ts);let D=Object.values(l).map(S=>{let A=n[`${S.trackId}/${S.boxId}`]||{};return fm({...S,trackName:A.trackName??null,label:A.label??null})}).sort((S,A)=>A.reworkChargedUsd+A.reworkPendingUsd-(S.reworkChargedUsd+S.reworkPendingUsd)||A.genUsd-S.genUsd);return{window:t,basis:`rework = lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old; fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause`,byEpoch:Object.fromEntries(D.map(S=>[S.key,S])),epochs:D,byProvider:Object.fromEntries(Object.entries(T).map(([S,A])=>[S,fm(A)])),byModel:Object.fromEntries(Object.entries(I).map(([S,A])=>[S,fm(A)])),buckets:Object.fromEntries(Object.entries(m).map(([S,A])=>[S,Vt(A)])),fixUsdTotal:Vt(Object.values(m).reduce((S,A)=>S+A,0)),fixes:y,verdicts:w,pendingTaps:x,commits:s.length}}var FD,UD,pm,Cv,Mv,Nv,HD,$r,mm,Pv,xi,WD,qD,VD,Rv,zD,JD,XD,Vt,dm,su=j(()=>{gr();FD=LD(jD),UD=64*1024*1024,pm=(e,t)=>FD("git",["-C",e,...t],{maxBuffer:UD,timeout:6e4}),Cv="Bostrat-Epoch",Mv="Bostrat-Model",Nv="Bostrat-Fix-Cause",HD=24*36e5,$r=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,mm=90,Pv=3,xi=Object.freeze(["defect","design","requirements","style"]),WD=new Set(["A","B","E"]);qD={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};VD=/^revert\b|^revert "/i,Rv=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,zD=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,JD=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;XD=[/(^|\/)\.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)\//];Vt=e=>Math.round(e*1e4)/1e4,dm=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import Ti from"node:fs/promises";import yt from"node:path";import t1 from"node:os";import n1 from"node:net";import{randomUUID as r1}from"node:crypto";import{execFile as o1}from"node:child_process";import{promisify as s1}from"node:util";function f1(e){for(let t of xm(e))if(!qv.test(t))return t;return null}function Kv(e){let t=xm(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function p1(e,t){let n=Kv(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return yt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return yt.resolve(e,n[r+1])}return e}function h1(e){let t=!1,n=!1;for(let r of xm(e)){if(!t){if(qv.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&hm(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,hm(r))return`serves the filesystem root ("${r}")`;continue}if(hm(r))return`serves the filesystem root ("${r}")`;if(m1(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function g1(e,t){if(yt.resolve(e)===yt.resolve(t))return!0;let[n,r]=await Promise.all([au(e),au(t)]);return n===r}async function y1(e){let n=(await Ti.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 Gv(yt.join(e,o.name,ym))?`./${o.name}`:null))).filter(Boolean)}async function b1(e,t){let[n,r]=await Promise.all([au(e),au(t)]),o=yt.relative(n,r);if(o&&(o.startsWith("..")||yt.isAbsolute(o)))throw vi(`serve directory escapes the Track workspace: ${t}`)}async function k1(e,t){let n=h1(t);if(n)throw vi(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await w1(e))throw vi(`serve directory does not exist: ${e}`);let r=f1(t);if(!r)return;if(!r.includes("/")&&!await Iy(r,{loginShell:!0,failOpen:!0}))throw vi(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(l1.test(t)||u1.has(r)||!c1.has(r)||!Kv(t).includes("run")||d1.test(t))return;let o=p1(e,t);if(await Gv(yt.join(o,ym)))return;let s=await y1(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw vi(`no ${ym} in ${o}${a}`)}async function Wn(){return dn||(wm||=(async()=>{try{dn=JSON.parse(await Ti.readFile(Wv,"utf8"))}catch{dn={views:[],adoptWatermarks:{}}}finally{wm=null}return dn.views||=[],dn.adoptWatermarks||={},dn})(),wm)}async function pn(){await wt(Wv,dn)}function Hn(e,t,n={}){return O({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 Sm(e){return Si.get(e)||null}function zv(e,{name:t,box:n}={}){let r=Si.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 Si.set(e,o),o}function bm(e,t){let n=Si.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function iu(e,{error:t=null}={}){let n=Si.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function Jv(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 km(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function x1(e,t){let n=km(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!Uv.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Uv.has(s))return s}}function Ai(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=n1.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 Tm(e){if(process.platform!=="darwin"){await fn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await fn("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 Yv(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function v1(e,t){let n=new Set(e.views.map(o=>o.port)),r=Yv();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=Lv;o<=Fv;o++)if(!(n.has(o)||r.has(o))&&!await Ai(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Lv}-${Fv}`)}async function S1(e){try{let{stdout:t}=await fn(W,q("list-sessions","-F","#{session_name}"));return t.split(`
|
|
194
|
-
`).some(n=>n.trim()===
|
|
197
|
+
`)}}async function hS(e,t,{fixKey:n=null,window:r=Dr,resolveOrigin:o=null,context:s={}}={}){let a=await uS(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=cS(a.message),l=n?pu(n):c.epoch,u=l?Us(l):null,{stdout:f}=await $m(e,["diff","-U0","--no-color","-M","-C",i,t]),d=KB(f),p=0,h=0,w=0,g=new Map;for(let B of d){if(B.binary)continue;let T=B.oldPath||B.newPath||"",_=B.deleted.reduce((I,[,U])=>I+U,0);if(qB(T)){w+=_;continue}if(p+=B.added,h+=_,!B.oldPath||!_)continue;let O;try{let I=["blame","-w","-M","-C","--line-porcelain"];for(let[U,H]of B.deleted)I.push("-L",`${U},${U+H-1}`);I.push(i,"--",B.oldPath),O=GB((await $m(e,I)).stdout)}catch{continue}for(let{sha:I,time:U}of O.values()){let H=g.get(I)??{lines:0,time:U};H.lines+=1,g.set(I,H)}}let k=[],S=0,A=new Map;for(let[B,{lines:T,time:_}]of g){let O=A.get(B);if(!O){try{O=await uS(e,B)}catch{O=null}A.set(B,O)}let I=O?cS(O.message):{epoch:null},U=I.epoch,H=I.provider,ae=I.model;if(!U&&o){let W=await o(B,O);W?.trackId&&W?.boxId!=null&&(U={trackId:W.trackId,boxId:W.boxId,epoch:W.epoch??0},H=H??W.provider??null,ae=ae??W.model??null)}if(!U){S+=T;continue}let J=Number.isFinite(_)?_:O?.ts??a.ts,C=Math.max(0,(a.ts-J)/NB),Q=Us(U),ve=Q===u?"self":BB(C,r);k.push({sha:B,key:Q,trackId:U.trackId,boxId:U.boxId,epoch:U.epoch,provider:H??null,model:ae??null,lines:T,ageDays:Math.round(C*100)/100,class:ve,decay:ve==="rework"?1:ve==="drift"?Math.round(jB(C,{window:r})*1e3)/1e3:0})}k.sort((B,T)=>T.lines-B.lines);let R=HB({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:p,deleted:h,excludedDeleted:w,origins:k,unattributedLines:S,ghost:h===0&&p>0,heuristic:R}}function wS(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Im(e){let t=e.genUsd;return{...e,genUsd:Xt(t),reworkChargedUsd:Xt(e.reworkChargedUsd),reworkPendingUsd:Xt(e.reworkPendingUsd),trueUsd:Xt(t+e.reworkChargedUsd),multiplier:t>0?Xt((t+e.reworkChargedUsd)/t):null}}function gS(e,{window:t=Dr,boxMeta:n={}}={}){let r=e||[],o=r.filter(T=>T.type==="cost.recorded"),s=r.filter(T=>T.type==="code.committed"),a=r.filter(T=>T.type==="code.rework"),i=new Map;for(let T of r)T.type==="rework.cause.answered"&&T.payload?.sha&&T.payload?.cause&&i.set(T.payload.sha,T.payload.cause);let c=new Set;for(let T of r)T.type==="rework.cause.offered"&&T.payload?.sha&&c.add(T.payload.sha);let l={},u=(T,_,O)=>Us({trackId:T,boxId:_,epoch:O??0}),f=(T,_={})=>{if(!l[T]){let I=pu(T)||{};l[T]={key:T,...I,provider:null,model:null,..._m()}}let O=l[T];return _.provider&&!O.provider&&(O.provider=_.provider),_.model&&!O.model&&(O.model=_.model),O};for(let T of o){if(!T.boxId)continue;let _=u(T.trackId,T.boxId,T.epoch),{usd:O,model:I}=Bt(T),U=f(_,{provider:T.provider,model:I!=="unknown"?I:null});U.genUsd+=O}let d=new Map;for(let T of s){let _=`${T.trackId}/${T.boxId}`;(d.get(_)??d.set(_,[]).get(_)).push(T)}for(let T of d.values())T.sort((_,O)=>_.ts-O.ts);let p=(T,_,O,I)=>{let U=d.get(`${T}/${_}`)||[],H=-1/0;for(let C of U)C.ts<I&&(H=C.ts);let ae=0,J=0;for(let C of o)C.trackId!==T||C.boxId!==_||(C.epoch??0)===(O??0)&&(C.ts<=H||C.ts>I||(ae+=Bt(C).usd,J++));return{usd:ae,turns:J,from:Number.isFinite(H)?H:null}},h={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0},w={A:0,B:0,C:0,E:0,none:0},g=[],k=[],S={},A={},R=new Set;for(let T of a){let _=T.payload||{};if(!_.sha||R.has(_.sha))continue;R.add(_.sha);let O=pu(_.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:I,turns:U}=p(O.trackId,O.boxId,O.epoch,_.ts??T.ts),H=null;i.has(_.sha)?H={cause:i.get(_.sha),level:"B",basis:"operator"}:(_.heuristic?.level&&_.heuristic.level!=="C"||_.heuristic)&&(H=_.heuristic);let ae=H?.level??"none";w[ae]=(w[ae]||0)+1;let J=PB.has(ae),C=Number(_.deleted)||0,Q=Array.isArray(_.origins)?_.origins:[],ve=Q.filter(be=>be.class==="rework").reduce((be,se)=>be+se.lines,0),W={sha:_.sha,subject:_.subject??"",ts:_.ts??T.ts,trackId:T.trackId,fixKey:_.fixKey??null,fixProvider:_.fixProvider??T.provider??null,fixUsd:Xt(I),fixTurns:U,linesReworked:ve,unattributedLines:Number(_.unattributedLines)||0,ghost:_.ghost===!0,verdict:H?{cause:H.cause,level:ae,basis:H.basis??null}:null,charged:Xt(0),origins:[]};if(_.ghost||C===0)h.ghost+=I;else{let be=0;for(let ue of Q){let me=ue.lines/C,j=I*me;if(be+=j,ue.class==="self"){h.self+=j;continue}let ie=ue.class==="rework"?1:Number(ue.decay)||0,le=j*ie;if(h.decayed+=j-le,le<=0)continue;let Ae=f(ue.key,{provider:ue.provider,model:ue.model});Ae.fixes+=1,Ae.linesReworked+=ue.class==="rework"?ue.lines:0;let $e=H?.cause&&Ii.includes(H.cause)?H.cause:"unclassified";Ae.causes[$e]+=1,J?(Ae.reworkChargedUsd+=le,h.charged+=le,W.charged=Xt(W.charged+le)):(Ae.reworkPendingUsd+=le,h.pending+=le),W.origins.push({key:ue.key,provider:Ae.provider,model:Ae.model,lines:ue.lines,ageDays:ue.ageDays,class:ue.class,usd:Xt(le)})}let se=Math.max(0,I-be);h.unattributed+=se}g.push(W),!i.has(_.sha)&&c.has(_.sha)&&k.push(_.sha)}for(let T of Object.values(l)){let _=T.provider||"unknown",O=T.model||"unknown",I=U=>{U.genUsd+=T.genUsd,U.reworkChargedUsd+=T.reworkChargedUsd,U.reworkPendingUsd+=T.reworkPendingUsd,U.linesReworked+=T.linesReworked,U.fixes+=T.fixes;for(let H of Object.keys(U.causes))U.causes[H]+=T.causes[H]};I(S[_]||=_m()),I(A[O]||=_m())}g.sort((T,_)=>_.ts-T.ts);let B=Object.values(l).map(T=>{let _=n[`${T.trackId}/${T.boxId}`]||{};return Im({...T,trackName:_.trackName??null,label:_.label??null})}).sort((T,_)=>_.reworkChargedUsd+_.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||_.genUsd-T.genUsd);return{window:t,basis:`rework = lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old; fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause`,byEpoch:Object.fromEntries(B.map(T=>[T.key,T])),epochs:B,byProvider:Object.fromEntries(Object.entries(S).map(([T,_])=>[T,Im(_)])),byModel:Object.fromEntries(Object.entries(A).map(([T,_])=>[T,Im(_)])),buckets:Object.fromEntries(Object.entries(h).map(([T,_])=>[T,Xt(_)])),fixUsdTotal:Xt(Object.values(h).reduce((T,_)=>T+_,0)),fixes:g,verdicts:w,pendingTaps:k,commits:s.length}}var CB,MB,$m,dS,fS,pS,NB,Dr,Rm,mS,Ii,PB,DB,LB,lS,FB,UB,WB,Xt,_m,hu=L(()=>{vr();CB=OB(RB),MB=64*1024*1024,$m=(e,t)=>CB("git",["-C",e,...t],{maxBuffer:MB,timeout:6e4}),dS="Bostrat-Epoch",fS="Bostrat-Model",pS="Bostrat-Fix-Cause",NB=24*36e5,Dr=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Rm=90,mS=3,Ii=Object.freeze(["defect","design","requirements","style"]),PB=new Set(["A","B","E"]);DB={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};LB=/^revert\b|^revert "/i,lS=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,FB=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,UB=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;WB=[/(^|\/)\.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)\//];Xt=e=>Math.round(e*1e4)/1e4,_m=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import Oi from"node:fs/promises";import Tt from"node:path";import VB from"node:os";import zB from"node:net";import{randomUUID as YB}from"node:crypto";import{execFile as JB}from"node:child_process";import{promisify as XB}from"node:util";function o1(e){for(let t of Bm(e))if(!SS.test(t))return t;return null}function TS(e){let t=Bm(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function s1(e,t){let n=TS(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Tt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Tt.resolve(e,n[r+1])}return e}function i1(e){let t=!1,n=!1;for(let r of Bm(e)){if(!t){if(SS.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Om(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Om(r))return`serves the filesystem root ("${r}")`;continue}if(Om(r))return`serves the filesystem root ("${r}")`;if(a1(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function l1(e,t){if(Tt.resolve(e)===Tt.resolve(t))return!0;let[n,r]=await Promise.all([wu(e),wu(t)]);return n===r}async function u1(e){let n=(await Oi.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 ES(Tt.join(e,o.name,Nm))?`./${o.name}`:null))).filter(Boolean)}async function d1(e,t){let[n,r]=await Promise.all([wu(e),wu(t)]),o=Tt.relative(n,r);if(o&&(o.startsWith("..")||Tt.isAbsolute(o)))throw $i(`serve directory escapes the Track workspace: ${t}`)}async function f1(e,t){let n=i1(t);if(n)throw $i(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await c1(e))throw $i(`serve directory does not exist: ${e}`);let r=o1(t);if(!r)return;if(!r.includes("/")&&!await Ky(r,{loginShell:!0,failOpen:!0}))throw $i(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(t1.test(t)||n1.has(r)||!e1.has(r)||!TS(t).includes("run")||r1.test(t))return;let o=s1(e,t);if(await ES(Tt.join(o,Nm)))return;let s=await u1(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw $i(`no ${Nm} in ${o}${a}`)}async function Vn(){return hn||(Cm||=(async()=>{try{hn=JSON.parse(await Oi.readFile(vS,"utf8"))}catch{hn={views:[],adoptWatermarks:{}}}finally{Cm=null}return hn.views||=[],hn.adoptWatermarks||={},hn})(),Cm)}async function gn(){await vt(vS,hn)}function Gn(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 Lm(e){return Ri.get(e)||null}function _S(e,{name:t,box:n}={}){let r=Ri.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 Ri.set(e,o),o}function Pm(e,t){let n=Ri.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function gu(e,{error:t=null}={}){let n=Ri.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function IS(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 Dm(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function p1(e,t){let n=Dm(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!kS.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!kS.has(s))return s}}function Ci(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=zB.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 Fm(e){if(process.platform!=="darwin"){await wn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await wn("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 $S(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function m1(e,t){let n=new Set(e.views.map(o=>o.port)),r=$S();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=yS;o<=bS;o++)if(!(n.has(o)||r.has(o))&&!await Ci(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${yS}-${bS}`)}async function h1(e){try{let{stdout:t}=await wn(K,G("list-sessions","-F","#{session_name}"));return t.split(`
|
|
198
|
+
`).some(n=>n.trim()===Hs(e))}catch{return!1}}async function Mi(e){try{await wn(K,G("kill-session","-t",Hs(e)))}catch{}}async function Um(e){if(!await Ua())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Hs(e.slug),n=e.serveCwd??e.cwd,r=Tt.dirname(process.execPath);await wn(K,G("new-session","-d","-s",t,"-c",n)),await wn(K,G("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await wn(K,G("send-keys","-t",t,"Enter")),await wn(K,G("send-keys","-t",t,"-l",e.cmd)),await wn(K,G("send-keys","-t",t,"Enter"))}async function xS(e,t=12){try{let{stdout:n}=await wn(K,G("capture-pane","-t",Hs(e),"-p"));return String(n||"").trimEnd().split(`
|
|
195
199
|
`).filter(Boolean).slice(-t).join(`
|
|
196
|
-
`)}catch{return""}}async function A1(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await S1(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await Hv(e.slug)||"server exited before listening"),Un.delete(e.id),await Hn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Ai(e.port))return Un.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Hn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>T1?(e.state="error",e.error=await Hv(e.slug)||"never started listening",await Hn(e,"error",{error:e.error}),!0):!1;let o=(Un.get(e.id)||0)+1;return Un.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function cu(){if(!gm){gm=!0;try{let e=await Wn(),t=!1;for(let n of e.views)await A1(n)&&(t=!0);t&&await pn()}finally{gm=!1}}}async function lu({trackId:e}={}){await cu();let t=await Wn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function Xv(e){let n=(await Wn()).views.find(r=>r.id===e);return n?{...n}:null}function Em(e){return dn&&dn.views.find(t=>t.slug===e)||null}async function _m({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=yt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await b1(r,u),await k1(u,n)},f=await Wn();if(i!=="detected"){let w=km(t),y=String(n).trim(),x=f.views.find(T=>T.trackId===e&&(T.slug===w||km(T.name)===w||T.cmd===y&&yt.resolve(T.serveCwd??T.cwd)===u));if(x)return E1(f,x,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(w=>w.state==="live"||w.state==="starting").length>=Rr)throw new Error(`view cap reached (${Rr} live) \u2014 stop one first`);let m={id:r1().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:x1(f,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await v1(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Am(m),f.views.push(m),await pn(),await Hn(m,"created",{origin:i}),bm(e,m.id),i==="detected"&&await Ai(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await pn(),await Hn(m,"live")),{...m}}async function E1(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 g1(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Ai(t.port))return await pn(),bm(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>=Rr)throw new Error(`view cap reached (${Rr} live) \u2014 stop one first`);return t.origin==="detected"?(await Tm(t.port),t.origin="agent"):await Ei(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Un.delete(t.id),await Am(t),await pn(),await Hn(t,"created",{restarted:!0,reused:!0}),bm(t.trackId,t.id),{...t,reused:!0}}async function Zv(e){let n=(await Wn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Tm(n.port):await Ei(n.slug),n.state!=="stopped"&&(n.state="stopped",Un.delete(n.id),await pn(),await Hn(n,"stopped")),{...n}}async function Qv(e){let t=await Wn(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=Rr)throw new Error(`view cap reached (${Rr} live) \u2014 stop one first`);return await Ei(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Un.delete(n.id),await Am(n),await pn(),await Hn(n,"created",{restarted:!0}),{...n}}async function eS(e){let t=await Wn(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await Tm(r.port):await Ei(r.slug),t.views.splice(n,1),Un.delete(r.id),await pn(),await Hn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function tS(e){let t=await Wn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Ei(r.slug),Un.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await pn()),n.length}async function nS(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Wn(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await pn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(_1)){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(!Yv().has(c)&&!n.views.some(l=>l.port===c)&&await Ai(c))try{i.push(await _m({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 fn,a1,Wv,Lv,Fv,Rr,i1,Ps,Uv,vi,c1,ym,l1,u1,d1,xm,qv,hm,m1,w1,Gv,au,dn,wm,Si,vm,Vv,Un,T1,gm,_1,uu=j(()=>{nt();ue();rr();fn=s1(o1),a1=process.env.BOSTRAT_STATE_DIR||yt.join(process.env.HOME||t1.tmpdir(),".bostrat"),Wv=yt.join(a1,"views.json"),[Lv,Fv]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Rr=Number(process.env.BOSTRAT_VIEW_MAX)||3,i1="bostrat-view-",Ps=e=>`${i1}${e}`,Uv=new Set(["bostrat","www","api","views","app"]),vi=e=>Object.assign(new Error(e),{status:400}),c1=new Set(["npm","pnpm","yarn","bun"]),ym="package.json",l1=/[|;&`]|\$\(/,u1=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),d1=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,xm=e=>String(e).trim().split(/\s+/).filter(Boolean),qv=/^[A-Za-z_][A-Za-z0-9_]*=/;hm=e=>e==="/",m1=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");w1=e=>Ti.stat(e).then(t=>t.isDirectory(),()=>!1),Gv=e=>Ti.stat(e).then(t=>t.isFile(),()=>!1),au=e=>Ti.realpath(e).catch(()=>yt.resolve(e));dn=null,wm=null;Si=new Map,vm=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Vv=12e3;Un=new Map,T1=12e4;gm=!1;_1=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function du(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>$1?null:n}function R1(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function rS(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await F(e)}catch{return}let r=du(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await wo(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Im.get(e.id)||0)<I1)return;Im.set(e.id,s);let a;try{a=await cn(e.cwd,{caller:"reconcile"})}catch{return}let i=await Os(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await xe(e.cwd).catch(()=>null);if(l){let{link:u,created:d}=await St({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!d&&!(u?.trackId===e.id&&await Nn(l,c.number))){if(!R1(u,r))return;let f=await F(e).catch(()=>null);if(f&&du(f)==null)return;await O(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await O(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),Im.delete(e.id)}var I1,Im,$1,$m=j(()=>{ue();$s();tr();Pn();gi();Cs();I1=6e3,Im=new Map,$1=30*6e4});import Rm from"node:os";import{execFile as O1}from"node:child_process";import{promisify as C1}from"node:util";async function fu(){if(process.platform!=="darwin")return Rm.freemem();try{let{stdout:e}=await M1("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:Rm.freemem()}catch{return Rm.freemem()}}var M1,Om=j(()=>{M1=C1(O1)});import We from"node:fs/promises";import Ke from"node:path";import iS from"node:os";import{randomUUID as N1}from"node:crypto";import{execFile as P1}from"node:child_process";import{promisify as D1}from"node:util";import{chromium as B1}from"playwright-core";async function wn(){return go||(Cm||=(async()=>{try{go=JSON.parse(await We.readFile(lS,"utf8"))}catch{go={mocks:[]}}finally{Cm=null}return go.mocks||=[],go})(),Cm)}async function Mt(){await wt(lS,go)}function pS(e,t){hn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&hn(e,t.v,"png")}function ut(e,t,n={}){return O({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 Nt(e,t){let n=Ii(e.cwd);return{html:Ke.join(n,`${e.id}.v${t}.html`),png:Ke.join(n,`${e.id}.v${t}.png`)}}function _i(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?Ke.join(e.cwd,r):null}function zt(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 Pm(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function pu(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(q1)}function K1(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 G1(e){let t=Buffer.alloc(24),n=await We.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function V1({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 z1(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>f&&(f=y),m<d&&(d=m),p=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},t.toString("base64"))}async function J1(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 Y1({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await B1.launch({executablePath:uS,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Tt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ds})).newPage();a.setDefaultTimeout(Bs),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Bs}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Bs});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await z1(a,i),u=l&&V1({...l,pad:F1*Ds,align:Ds});u&&(i=await J1(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<dS)throw new Tt(`capture produced a ${i.length}-byte image`);let c=Pm(i);if(!c)throw new Tt("output is not a PNG");return await We.writeFile(t,i),{width:Math.round(c.width/Ds),height:Math.round(c.height/Ds),dpr:Ds,renderMs:Date.now()-r}}catch(s){throw s instanceof Tt?s:new Tt(/timeout/i.test(s?.message||"")?`render timed out after ${Bs}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function aS({htmlPath:e,outPath:t,width:n}){let r=await We.mkdtemp(Ke.join(iS.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Nm(oS,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Bs,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Tt(`firefox not found (${oS})`,""):new Tt(c?.killed?`render timed out after ${Bs}ms`:"firefox exited non-zero",s)}let a=await We.stat(t).catch(()=>null);if(!a||a.size<dS)throw new Tt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Pm(await G1(t));if(!i)throw new Tt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await We.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function X1(e){if(j1==="firefox")return aS(e);try{return await Y1(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await aS(e)}catch(n){throw/not found/.test(n?.message||"")?new Tt(`no browser installed \u2014 chromium missing at ${uS} 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 mS(e){return Mm=Mm.then(e,e),Mm}async function hS(e,t){let n=await wn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Mt(),await ut(r,"rendering",{version:t});try{let o=await fu();if(o<U1)throw new Tt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Nt(r,t),a=await Z1({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await We.rm(s.png,{force:!0}).catch(()=>{});return}Dm(r,{v:t,source:"html",htmlRel:Ke.relative(r.cwd,s.html),pngRel:Ke.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Mt(),await ut(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),mn(r),hn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Mt(),await ut(r,"error",{version:t,error:r.error}),mn(r)}}}function Dm(e,t){e.versions=(e.versions||[]).filter(r=>r.v!==t.v).concat(t),e.versions.sort((r,o)=>r.v-o.v);let n=e.versions[e.versions.length-1];e.readyVersion=n.v,e.height=n.height,e.dpr=n.dpr,e.renderMs=n.renderMs??null,n.v===e.version&&(e.state="ready",e.error=null)}function Q1({trackId:e,name:t,cwd:n,width:r}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?L1:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function $i({trackId:e,group:t}={}){let n=await wn(),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 wn()).mocks.find(r=>r.id===e);return n?{...n}:null}function wS({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&&!pu(t))throw new Error("image must be a PNG");if(n){let o=K1(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>sS)throw new Error(`video must be ${sS/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function gS({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=Q1({trackId:e,name:t,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=wS({html:n,image:r,video:o}),m=await wn(),w={id:N1().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 We.mkdir(Ii(s),{recursive:!0}),m.mocks.push(w),f?(await We.writeFile(Nt(w,1).html,n),await Mt(),await ut(w,"created"),mn(w),hn(w,1,"html"),mS(()=>hS(w.id,1))):o?(await bS(w,1,o,p),await Mt(),await ut(w,"created"),await ut(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),mn(w),pS(w,w.versions[0])):(await kS(w,1,r),await Mt(),await ut(w,"created"),await ut(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),mn(w),hn(w,1,"png")),{...w}}async function yS(e,{html:t,image:n,video:r,width:o}={}){let a=(await wn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=wS({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 We.mkdir(Ii(a.cwd),{recursive:!0}),i?(a.state="queued",await We.writeFile(Nt(a,l).html,t),await Mt(),await ut(a,"updated",{version:l}),mn(a),hn(a,l,"html"),mS(()=>hS(a.id,l))):r?(await bS(a,l,r,c),await Mt(),await ut(a,"updated",{version:l}),await ut(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:zt(a)?.durationS??null}),mn(a),pS(a,a.versions.find(u=>u.v===l))):(await kS(a,l,n),await Mt(),await ut(a,"updated",{version:l}),await ut(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),mn(a),hn(a,l,"png")),{...a}}async function eB(e){try{let{stdout:t}=await Nm(H1,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:fS,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 tB(e,t){try{await Nm(W1,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:fS,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await We.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function bS(e,t,n,r){let o=Ii(e.cwd),s=Ke.join(o,`${e.id}.v${t}.${r.ext}`),a=Ke.join(o,`${e.id}.v${t}.poster.png`);await We.writeFile(s,n);let i=await eB(s),c=await tB(s,a);Dm(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Ke.relative(e.cwd,s),posterRel:c?Ke.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 kS(e,t,n){let r=Pm(n);if(!r)throw new Error("image must be a PNG");let o=Nt(e,t);await We.writeFile(o.png,n),Dm(e,{v:t,source:"image",htmlRel:null,pngRel:Ke.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function xS(e){let t=await wn(),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 ut(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function vS(e){let t=await wn(),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=Nt(r,o);await We.rm(s.html,{force:!0}).catch(()=>{}),await We.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await We.rm(Ke.join(r.cwd,s),{force:!0}).catch(()=>{});return await Mt(),await ut(r,"deleted"),Ft("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function SS(e){let n=(await wn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Ke.join(cS,"mock-archive",e.id),o=Ii(e.cwd),s=Ke.join(r,".bostrat","mocks");await We.mkdir(s,{recursive:!0});let a=await We.readdir(o).catch(()=>[]);for(let i of a)await We.copyFile(Ke.join(o,i),Ke.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 We.stat(Ke.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(),mn(i)}return await Mt(),n.length}async function TS(e){let t=await wn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Mt();for(let r of n)Ft("mocks",r.id).catch(()=>{})}return n.length}async function AS(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await wn();for(let r of n.mocks){await mn(r);for(let o of r.versions||[])o.htmlRel&&await hn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await hn(r,o.v,"png"),o.videoRel&&await hn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Nm,cS,lS,j1,uS,oS,L1,Ds,F1,dS,Bs,U1,sS,H1,W1,fS,Tt,go,Cm,mn,hn,Ii,q1,Z1,Mm,js=j(()=>{ue();rr();Om();sr();Nm=D1(P1),cS=process.env.BOSTRAT_STATE_DIR||Ke.join(process.env.HOME||iS.tmpdir(),".bostrat"),lS=Ke.join(cS,"mocks.json"),j1=process.env.BOSTRAT_MOCK_ENGINE||"chromium",uS=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",oS=process.env.BOSTRAT_FIREFOX_BIN||"firefox",L1=1300,Ds=2,F1=32,dS=1500,Bs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,U1=700*1024*1024,sS=100*1024*1024,H1=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",W1=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",fS=2e4,Tt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
200
|
+
`)}catch{return""}}async function g1(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await h1(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await xS(e.slug)||"server exited before listening"),Kn.delete(e.id),await Gn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Ci(e.port))return Kn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Gn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>w1?(e.state="error",e.error=await xS(e.slug)||"never started listening",await Gn(e,"error",{error:e.error}),!0):!1;let o=(Kn.get(e.id)||0)+1;return Kn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function yu(){if(!Mm){Mm=!0;try{let e=await Vn(),t=!1;for(let n of e.views)await g1(n)&&(t=!0);t&&await gn()}finally{Mm=!1}}}async function bu({trackId:e}={}){await yu();let t=await Vn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function RS(e){let n=(await Vn()).views.find(r=>r.id===e);return n?{...n}:null}function Hm(e){return hn&&hn.views.find(t=>t.slug===e)||null}async function Wm({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=Tt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await d1(r,u),await f1(u,n)},d=await Vn();if(i!=="detected"){let w=Dm(t),g=String(n).trim(),k=d.views.find(S=>S.trackId===e&&(S.slug===w||Dm(S.name)===w||S.cmd===g&&Tt.resolve(S.serveCwd??S.cwd)===u));if(k)return y1(d,k,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);let h={id:YB().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:p1(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await m1(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Um(h),d.views.push(h),await gn(),await Gn(h,"created",{origin:i}),Pm(e,h.id),i==="detected"&&await Ci(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await gn(),await Gn(h,"live")),{...h}}async function y1(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 l1(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Ci(t.port))return await gn(),Pm(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);return t.origin==="detected"?(await Fm(t.port),t.origin="agent"):await Mi(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Kn.delete(t.id),await Um(t),await gn(),await Gn(t,"created",{restarted:!0,reused:!0}),Pm(t.trackId,t.id),{...t,reused:!0}}async function OS(e){let n=(await Vn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Fm(n.port):await Mi(n.slug),n.state!=="stopped"&&(n.state="stopped",Kn.delete(n.id),await gn(),await Gn(n,"stopped")),{...n}}async function CS(e){let t=await Vn(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);return await Mi(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Kn.delete(n.id),await Um(n),await gn(),await Gn(n,"created",{restarted:!0}),{...n}}async function MS(e){let t=await Vn(),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 Fm(r.port):await Mi(r.slug),t.views.splice(n,1),Kn.delete(r.id),await gn(),await Gn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function NS(e){let t=await Vn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Mi(r.slug),Kn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await gn()),n.length}async function PS(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Vn(),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 gn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(b1)){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(!$S().has(c)&&!n.views.some(l=>l.port===c)&&await Ci(c))try{i.push(await Wm({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 wn,ZB,vS,yS,bS,Br,QB,Hs,kS,$i,e1,Nm,t1,n1,r1,Bm,SS,Om,a1,c1,ES,wu,hn,Cm,Ri,jm,AS,Kn,w1,Mm,b1,ku=L(()=>{lt();de();cr();wn=XB(JB),ZB=process.env.BOSTRAT_STATE_DIR||Tt.join(process.env.HOME||VB.tmpdir(),".bostrat"),vS=Tt.join(ZB,"views.json"),[yS,bS]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Br=Number(process.env.BOSTRAT_VIEW_MAX)||3,QB="bostrat-view-",Hs=e=>`${QB}${e}`,kS=new Set(["bostrat","www","api","views","app"]),$i=e=>Object.assign(new Error(e),{status:400}),e1=new Set(["npm","pnpm","yarn","bun"]),Nm="package.json",t1=/[|;&`]|\$\(/,n1=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),r1=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Bm=e=>String(e).trim().split(/\s+/).filter(Boolean),SS=/^[A-Za-z_][A-Za-z0-9_]*=/;Om=e=>e==="/",a1=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");c1=e=>Oi.stat(e).then(t=>t.isDirectory(),()=>!1),ES=e=>Oi.stat(e).then(t=>t.isFile(),()=>!1),wu=e=>Oi.realpath(e).catch(()=>Tt.resolve(e));hn=null,Cm=null;Ri=new Map,jm=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,AS=12e3;Kn=new Map,w1=12e4;Mm=!1;b1=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function xu(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>x1?null:n}function v1(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function DS(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await F(e)}catch{return}let r=xu(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await To(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(qm.get(e.id)||0)<k1)return;qm.set(e.id,s);let a;try{a=await fn(e.cwd,{caller:"reconcile"})}catch{return}let i=await js(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await Se(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await It({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await jn(l,c.number))){if(!v1(u,r))return;let d=await F(e).catch(()=>null);if(d&&xu(d)==null)return;await $(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await $(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),qm.delete(e.id)}var k1,qm,x1,Km=L(()=>{de();Bs();ar();Ln();vi();Eo();k1=6e3,qm=new Map,x1=30*6e4});import Gm from"node:os";import{execFile as S1}from"node:child_process";import{promisify as T1}from"node:util";async function vu(){if(process.platform!=="darwin")return Gm.freemem();try{let{stdout:e}=await E1("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:Gm.freemem()}catch{return Gm.freemem()}}var E1,Vm=L(()=>{E1=T1(S1)});import qe from"node:fs/promises";import Ve from"node:path";import FS from"node:os";import{randomUUID as A1}from"node:crypto";import{execFile as _1}from"node:child_process";import{promisify as I1}from"node:util";import{chromium as $1}from"playwright-core";async function kn(){return Io||(zm||=(async()=>{try{Io=JSON.parse(await qe.readFile(HS,"utf8"))}catch{Io={mocks:[]}}finally{zm=null}return Io.mocks||=[],Io})(),zm)}async function Lt(){await vt(HS,Io)}function GS(e,t){bn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&bn(e,t.v,"png")}function gt(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 Ft(e,t){let n=Pi(e.cwd);return{html:Ve.join(n,`${e.id}.v${t}.html`),png:Ve.join(n,`${e.id}.v${t}.png`)}}function Ni(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Ve.join(e.cwd,r):null}function Zt(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 Xm(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Su(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(D1)}function B1(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 j1(e){let t=Buffer.alloc(24),n=await qe.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function L1({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 F1(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 U1(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 H1({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await $1.launch({executablePath:WS,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new $t("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ws})).newPage();a.setDefaultTimeout(qs),await a.goto(`file://${e}`,{waitUntil:"load",timeout:qs}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:qs});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await F1(a,i),u=l&&L1({...l,pad:C1*Ws,align:Ws});u&&(i=await U1(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<qS)throw new $t(`capture produced a ${i.length}-byte image`);let c=Xm(i);if(!c)throw new $t("output is not a PNG");return await qe.writeFile(t,i),{width:Math.round(c.width/Ws),height:Math.round(c.height/Ws),dpr:Ws,renderMs:Date.now()-r}}catch(s){throw s instanceof $t?s:new $t(/timeout/i.test(s?.message||"")?`render timed out after ${qs}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function LS({htmlPath:e,outPath:t,width:n}){let r=await qe.mkdtemp(Ve.join(FS.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Jm(BS,["--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 $t(`firefox not found (${BS})`,""):new $t(c?.killed?`render timed out after ${qs}ms`:"firefox exited non-zero",s)}let a=await qe.stat(t).catch(()=>null);if(!a||a.size<qS)throw new $t(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Xm(await j1(t));if(!i)throw new $t("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await qe.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function W1(e){if(R1==="firefox")return LS(e);try{return await H1(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await LS(e)}catch(n){throw/not found/.test(n?.message||"")?new $t(`no browser installed \u2014 chromium missing at ${WS} 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 VS(e){return Ym=Ym.then(e,e),Ym}async function zS(e,t){let n=await kn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Lt(),await gt(r,"rendering",{version:t});try{let o=await vu();if(o<M1)throw new $t(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Ft(r,t),a=await q1({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await qe.rm(s.png,{force:!0}).catch(()=>{});return}Zm(r,{v:t,source:"html",htmlRel:Ve.relative(r.cwd,s.html),pngRel:Ve.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Lt(),await gt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),yn(r),bn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Lt(),await gt(r,"error",{version:t,error:r.error}),yn(r)}}}function Zm(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 K1({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?O1:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Di({trackId:e,group:t}={}){let n=await kn(),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 zn(e){let n=(await kn()).mocks.find(r=>r.id===e);return n?{...n}:null}function YS({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&&!Su(t))throw new Error("image must be a PNG");if(n){let o=B1(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>jS)throw new Error(`video must be ${jS/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function JS({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=K1({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=YS({html:n,image:r,video:o}),h=await kn(),w={id:A1().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 qe.mkdir(Pi(s),{recursive:!0}),h.mocks.push(w),d?(await qe.writeFile(Ft(w,1).html,n),await Lt(),await gt(w,"created"),yn(w),bn(w,1,"html"),VS(()=>zS(w.id,1))):o?(await ZS(w,1,o,p),await Lt(),await gt(w,"created"),await gt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),yn(w),GS(w,w.versions[0])):(await QS(w,1,r),await Lt(),await gt(w,"created"),await gt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),yn(w),bn(w,1,"png")),{...w}}async function XS(e,{html:t,image:n,video:r,width:o}={}){let a=(await kn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=YS({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 qe.mkdir(Pi(a.cwd),{recursive:!0}),i?(a.state="queued",await qe.writeFile(Ft(a,l).html,t),await Lt(),await gt(a,"updated",{version:l}),yn(a),bn(a,l,"html"),VS(()=>zS(a.id,l))):r?(await ZS(a,l,r,c),await Lt(),await gt(a,"updated",{version:l}),await gt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Zt(a)?.durationS??null}),yn(a),GS(a,a.versions.find(u=>u.v===l))):(await QS(a,l,n),await Lt(),await gt(a,"updated",{version:l}),await gt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),yn(a),bn(a,l,"png")),{...a}}async function G1(e){try{let{stdout:t}=await Jm(N1,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:KS,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 V1(e,t){try{await Jm(P1,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:KS,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await qe.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function ZS(e,t,n,r){let o=Pi(e.cwd),s=Ve.join(o,`${e.id}.v${t}.${r.ext}`),a=Ve.join(o,`${e.id}.v${t}.poster.png`);await qe.writeFile(s,n);let i=await G1(s),c=await V1(s,a);Zm(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Ve.relative(e.cwd,s),posterRel:c?Ve.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 QS(e,t,n){let r=Xm(n);if(!r)throw new Error("image must be a PNG");let o=Ft(e,t);await qe.writeFile(o.png,n),Zm(e,{v:t,source:"image",htmlRel:null,pngRel:Ve.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function eT(e){let t=await kn(),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 gt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function tT(e){let t=await kn(),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=Ft(r,o);await qe.rm(s.html,{force:!0}).catch(()=>{}),await qe.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await qe.rm(Ve.join(r.cwd,s),{force:!0}).catch(()=>{});return await Lt(),await gt(r,"deleted"),qt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function nT(e){let n=(await kn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Ve.join(US,"mock-archive",e.id),o=Pi(e.cwd),s=Ve.join(r,".bostrat","mocks");await qe.mkdir(s,{recursive:!0});let a=await qe.readdir(o).catch(()=>[]);for(let i of a)await qe.copyFile(Ve.join(o,i),Ve.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 qe.stat(Ve.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(),yn(i)}return await Lt(),n.length}async function rT(e){let t=await kn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Lt();for(let r of n)qt("mocks",r.id).catch(()=>{})}return n.length}async function oT(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await nt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await kn();for(let r of n.mocks){await yn(r);for(let o of r.versions||[])o.htmlRel&&await bn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await bn(r,o.v,"png"),o.videoRel&&await bn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Jm,US,HS,R1,WS,BS,O1,Ws,C1,qS,qs,M1,jS,N1,P1,KS,$t,Io,zm,yn,bn,Pi,D1,q1,Ym,Ks=L(()=>{de();cr();Vm();ur();Jm=I1(_1),US=process.env.BOSTRAT_STATE_DIR||Ve.join(process.env.HOME||FS.tmpdir(),".bostrat"),HS=Ve.join(US,"mocks.json"),R1=process.env.BOSTRAT_MOCK_ENGINE||"chromium",WS=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",BS=process.env.BOSTRAT_FIREFOX_BIN||"firefox",O1=1300,Ws=2,C1=32,qS=1500,qs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,M1=700*1024*1024,jS=100*1024*1024,N1=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",P1=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",KS=2e4,$t=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
197
201
|
${n.trim().split(`
|
|
198
202
|
`).slice(-6).join(`
|
|
199
|
-
`)}`:t),this.name="MockRenderError"}},go=null,Cm=null;mn=e=>ct("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),hn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?_i(e,r,"video"):r&&_i(e,r,n)||(n==="html"?Nt(e,t).html:null);return o?or("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ii=e=>Ke.join(e,".bostrat","mocks");q1=Buffer.from([137,80,78,71,13,10,26,10]);Z1=X1,Mm=Promise.resolve()});import nB from"node:fs/promises";import RS from"node:path";import rB from"node:os";import{randomUUID as OS}from"node:crypto";async function Gn(){return Kn||(Bm||=(async()=>{let e=!0;try{Kn=JSON.parse(await nB.readFile(CS,"utf8"))}catch{Kn={flows:[]},e=!1}finally{Bm=null}if(Kn.flows||=[],!e&&Me()){let{records:t,source:n}=await Ye("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Kn.flows.push(r.data);Kn.flows.length&&await Ls().catch(()=>{})}}return Kn})(),Bm)}async function Ls(){await wt(CS,Kn)}function jm(e,t,n={}){return O({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 MS({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>ES)throw new Error(`steps must be an array of 1-${ES}`);let r=await $i({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>IS)throw new Error(`${l}: caption over ${IS} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>$S)throw new Error(`${l}: title over ${$S} chars`);let f=String(c.mock??"").trim().replace(/^mock:/,"");if(!f)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(T=>T.id===f);if(p.length||(p=r.filter(T=>T.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(T=>`mock:${T.id}`).join(", ")}) \u2014 use the id`);let m=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(T=>T.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(T=>T.mockId===m.id),x=y>=0?o.splice(y,1)[0].id:OS().slice(0,8);s.push({id:x,mockId:m.id,caption:u,title:d,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function sB(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>_S)throw new Error(`name over ${_S} chars`);return e.trim()}async function NS({trackId:e}={}){let t=await Gn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function aB(e){let n=(await Gn()).flows.find(r=>r.id===e);return n?{...n}:null}async function PS(e){let t=await aB(e);if(!t)return null;let n=new Map((await $i({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=zt(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 DS({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=sB(t),i=await Gn(),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 MS({trackId:e,steps:n}),d=Date.now(),f={id:OS().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 Ls(),await jm(f,"created"),mu(f),{...f}}async function BS(e,{steps:t,note:n}={}){let o=(await Gn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await MS({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await Ls(),await jm(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),mu(o),{...o}}async function jS(e){let t=await Gn(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Ls(),await jm(r,"deleted"),Ft("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function LS(e){return(await Gn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function FS(e){let n=(await Gn()).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(),mu(r);return n.length&&await Ls(),n.length}async function US(e){let t=await Gn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Ls();for(let r of n)Ft("flows",r.id).catch(()=>{})}return n.length}async function HS(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Gn();for(let r of n.flows)await mu(r);return{pushed:n.flows.length,source:"plane"}}var oB,CS,ES,_S,IS,$S,Kn,Bm,mu,Lm=j(()=>{ue();rr();js();sr();oB=process.env.BOSTRAT_STATE_DIR||RS.join(process.env.HOME||rB.tmpdir(),".bostrat"),CS=RS.join(oB,"flows.json"),ES=20,_S=80,IS=500,$S=80,Kn=null,Bm=null;mu=e=>ct("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Jn from"node:fs/promises";import Yn from"node:path";import iB from"node:os";import{randomUUID as cB}from"node:crypto";function Ri(e){return lB.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function gn(){return Vn||(Fm||=(async()=>{let e=!0;try{Vn=JSON.parse(await Jn.readFile(qS,"utf8"))}catch{Vn={runbooks:[]},e=!1}finally{Fm=null}return Vn.runbooks||=[],!e&&Me()&&await uB().catch(()=>{}),Vn})(),Fm)}async function bo(){await wt(qS,Vn)}function wu(e){return Oi(ct("runbooks",{...e},{trackId:e.trackId}))}async function uB(){let{records:e,source:t}=await Ye("runbooks");if(!(t!=="plane"||!e.length)){await Jn.mkdir(hu,{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 $a("runbooks",r.id,o.v,"md");typeof s=="string"&&await Jn.writeFile(zn(r,o.v),s).catch(()=>{})}Vn.runbooks.push(r)}Vn.runbooks.length&&await bo()}}function Fs(e,t,n={}){return O({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 zn(e,t){return Yn.join(hu,`${e.id}.v${t}.md`)}function KS(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 GS(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>WS)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${WS/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ri(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 dB(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function fB({trackId:e,status:t}={}){let n=await gn(),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 VS({trackId:e,status:t}={}){let n=await gn(),r=await fB({trackId:e,status:t});if(!Me())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Ye("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 Um(e){let t=await pB(e);if(t)return t;if(!Me())return null;let{records:n,source:r}=await Ye("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 zS(e,t){let n=await $a("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function pB(e){let n=(await gn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function JS({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=GS(r),c=await gn(),l={id:cB().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 Jn.mkdir(hu,{recursive:!0}),await Jn.writeFile(zn(l,1),r),l.versions.push({v:1,mdRel:Yn.relative(yo,zn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await bo(),await Fs(l,"created",{version:1}),wu(l),Oi(or("runbooks",l.id,1,"md",zn(l,1))),{...l}}async function YS(e,{md:t,note:n}={}){let o=(await gn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");dB(o,"update");let s=GS(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Jn.mkdir(hu,{recursive:!0}),await Jn.writeFile(zn(o,a),t),o.versions.push({v:a,mdRel:Yn.relative(yo,zn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await bo(),await Fs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),wu(o),Oi(or("runbooks",o.id,a,"md",zn(o,a))),{...o}}async function XS(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await gn()).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 bo(),await Fs(r,t==="done"?"done":"reopened"),wu(r),{...r})}async function ZS(e){let t=await gn(),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 Jn.rm(Yn.join(yo,o.mdRel),{force:!0}).catch(()=>{});return await bo(),r.trackDeletedAt||await Fs(r,"deleted"),Oi(Ft("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function QS(e){let n=(await gn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Fs(r,"orphaned"),r.cwd=null,wu(r);return n.length&&await bo(),n.length}async function eT(e){let t=await gn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Fs(r,"deleted");for(let o of r.versions||[])await Jn.rm(Yn.join(yo,o.mdRel),{force:!0}).catch(()=>{});Oi(Ft("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await bo()),n.length}async function tT(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await gn();for(let r of n.runbooks){await ct("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await or("runbooks",r.id,o.v,"md",Yn.join(yo,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var yo,qS,hu,WS,lB,Vn,Fm,Oi,gu=j(()=>{rr();ue();sr();yo=process.env.BOSTRAT_STATE_DIR||Yn.join(process.env.HOME||iB.tmpdir(),".bostrat"),qS=Yn.join(yo,"runbooks.json"),hu=Yn.join(yo,"runbooks"),WS=256*1024,lB=[{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/}];Vn=null,Fm=null;Oi=e=>Promise.resolve(e).catch(()=>{})});import Hm from"node:fs/promises";import Ci from"node:path";import{randomUUID as mB}from"node:crypto";function qm(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return hB.has(t)?"image":wB.has(t)?"doc":null}function gB(e){let r=Ci.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 oT(e,{filename:t,buffer:n}){if(!e)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>nT)throw new Error(`file too large \u2014 max ${Math.floor(nT/(1024*1024))} MB`);let r=gB(t),o=Ci.extname(r).slice(1),s=qm(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=mB().slice(0,8),i=Wm(e);await Hm.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Hm.writeFile(Ci.join(i,c),n,{mode:384}),await Hm.chmod(Ci.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${rT}/${c}`,kind:s,size:n.length}}function sT(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Km(e){return e.role==="user"&&yB.test((e.text||"").trim())}var rT,Wm,nT,hB,wB,yB,Gm=j(()=>{rT=".bostrat/uploads",Wm=e=>Ci.join(e,rT),nT=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,hB=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),wB=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"]);yB=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function ko(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var aT=j(()=>{});import{execFile as bB}from"node:child_process";import{promisify as kB}from"node:util";function Vm(e){return`refs/bostrat/wip/${e}`}function SB(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function lT(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function TB(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:vB}async function zm(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await Or(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Or(o,["remote","get-url","origin"])}catch{return iT.has(o)||(iT.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 ws(o,"bostrat: wip snapshot"),{stdout:a}=await Or(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&yu.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=Vm(r);return await Or(o,["push","--force","origin",`${s}:${c}`],cT),yu.set(o,i),await O(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(`
|
|
200
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
201
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
203
|
+
`)}`:t),this.name="MockRenderError"}},Io=null,zm=null;yn=e=>ht("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),bn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Ni(e,r,"video"):r&&Ni(e,r,n)||(n==="html"?Ft(e,t).html:null);return o?lr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Pi=e=>Ve.join(e,".bostrat","mocks");D1=Buffer.from([137,80,78,71,13,10,26,10]);q1=W1,Ym=Promise.resolve()});import z1 from"node:fs/promises";import lT from"node:path";import Y1 from"node:os";import{randomUUID as uT}from"node:crypto";async function Jn(){return Yn||(Qm||=(async()=>{let e=!0;try{Yn=JSON.parse(await z1.readFile(dT,"utf8"))}catch{Yn={flows:[]},e=!1}finally{Qm=null}if(Yn.flows||=[],!e&&Me()){let{records:t,source:n}=await nt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Yn.flows.push(r.data);Yn.flows.length&&await Gs().catch(()=>{})}}return Yn})(),Qm)}async function Gs(){await vt(dT,Yn)}function eh(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 fT({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>sT)throw new Error(`steps must be an array of 1-${sT}`);let r=await Di({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>iT)throw new Error(`${l}: caption over ${iT} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>cT)throw new Error(`${l}: title over ${cT} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===d);if(p.length||(p=r.filter(S=>S.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let h=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(S=>S.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let g=o.findIndex(S=>S.mockId===h.id),k=g>=0?o.splice(g,1)[0].id:uT().slice(0,8);s.push({id:k,mockId:h.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function X1(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>aT)throw new Error(`name over ${aT} chars`);return e.trim()}async function pT({trackId:e}={}){let t=await Jn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function Z1(e){let n=(await Jn()).flows.find(r=>r.id===e);return n?{...n}:null}async function mT(e){let t=await Z1(e);if(!t)return null;let n=new Map((await Di({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=Zt(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 hT({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=X1(t),i=await Jn(),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 fT({trackId:e,steps:n}),f=Date.now(),d={id:uT().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await Gs(),await eh(d,"created"),Tu(d),{...d}}async function wT(e,{steps:t,note:n}={}){let o=(await Jn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await fT({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await Gs(),await eh(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Tu(o),{...o}}async function gT(e){let t=await Jn(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Gs(),await eh(r,"deleted"),qt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function yT(e){return(await Jn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function bT(e){let n=(await Jn()).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(),Tu(r);return n.length&&await Gs(),n.length}async function kT(e){let t=await Jn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Gs();for(let r of n)qt("flows",r.id).catch(()=>{})}return n.length}async function xT(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await nt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Jn();for(let r of n.flows)await Tu(r);return{pushed:n.flows.length,source:"plane"}}var J1,dT,sT,aT,iT,cT,Yn,Qm,Tu,th=L(()=>{de();cr();Ks();ur();J1=process.env.BOSTRAT_STATE_DIR||lT.join(process.env.HOME||Y1.tmpdir(),".bostrat"),dT=lT.join(J1,"flows.json"),sT=20,aT=80,iT=500,cT=80,Yn=null,Qm=null;Tu=e=>ht("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Qn from"node:fs/promises";import er from"node:path";import Q1 from"node:os";import{randomUUID as ej}from"node:crypto";function Bi(e){return tj.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function xn(){return Xn||(nh||=(async()=>{let e=!0;try{Xn=JSON.parse(await Qn.readFile(ST,"utf8"))}catch{Xn={runbooks:[]},e=!1}finally{nh=null}return Xn.runbooks||=[],!e&&Me()&&await nj().catch(()=>{}),Xn})(),nh)}async function Ro(){await vt(ST,Xn)}function Au(e){return ji(ht("runbooks",{...e},{trackId:e.trackId}))}async function nj(){let{records:e,source:t}=await nt("runbooks");if(!(t!=="plane"||!e.length)){await Qn.mkdir(Eu,{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 Na("runbooks",r.id,o.v,"md");typeof s=="string"&&await Qn.writeFile(Zn(r,o.v),s).catch(()=>{})}Xn.runbooks.push(r)}Xn.runbooks.length&&await Ro()}}function Vs(e,t,n={}){return $({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Zn(e,t){return er.join(Eu,`${e.id}.v${t}.md`)}function TT(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 ET(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>vT)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${vT/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Bi(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 rj(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function oj({trackId:e,status:t}={}){let n=await xn(),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 AT({trackId:e,status:t}={}){let n=await xn(),r=await oj({trackId:e,status:t});if(!Me())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await nt("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 rh(e){let t=await sj(e);if(t)return t;if(!Me())return null;let{records:n,source:r}=await nt("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 _T(e,t){let n=await Na("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function sj(e){let n=(await xn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function IT({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=ET(r),c=await xn(),l={id:ej().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 Qn.mkdir(Eu,{recursive:!0}),await Qn.writeFile(Zn(l,1),r),l.versions.push({v:1,mdRel:er.relative($o,Zn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Ro(),await Vs(l,"created",{version:1}),Au(l),ji(lr("runbooks",l.id,1,"md",Zn(l,1))),{...l}}async function $T(e,{md:t,note:n}={}){let o=(await xn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");rj(o,"update");let s=ET(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Qn.mkdir(Eu,{recursive:!0}),await Qn.writeFile(Zn(o,a),t),o.versions.push({v:a,mdRel:er.relative($o,Zn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Ro(),await Vs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Au(o),ji(lr("runbooks",o.id,a,"md",Zn(o,a))),{...o}}async function RT(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await xn()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await Ro(),await Vs(r,t==="done"?"done":"reopened"),Au(r),{...r})}async function OT(e){let t=await xn(),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 Qn.rm(er.join($o,o.mdRel),{force:!0}).catch(()=>{});return await Ro(),r.trackDeletedAt||await Vs(r,"deleted"),ji(qt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function CT(e){let n=(await xn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Vs(r,"orphaned"),r.cwd=null,Au(r);return n.length&&await Ro(),n.length}async function MT(e){let t=await xn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Vs(r,"deleted");for(let o of r.versions||[])await Qn.rm(er.join($o,o.mdRel),{force:!0}).catch(()=>{});ji(qt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Ro()),n.length}async function NT(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await nt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await xn();for(let r of n.runbooks){await ht("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await lr("runbooks",r.id,o.v,"md",er.join($o,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var $o,ST,Eu,vT,tj,Xn,nh,ji,_u=L(()=>{cr();de();ur();$o=process.env.BOSTRAT_STATE_DIR||er.join(process.env.HOME||Q1.tmpdir(),".bostrat"),ST=er.join($o,"runbooks.json"),Eu=er.join($o,"runbooks"),vT=256*1024,tj=[{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/}];Xn=null,nh=null;ji=e=>Promise.resolve(e).catch(()=>{})});import oh from"node:fs/promises";import Li from"node:path";import{randomUUID as aj}from"node:crypto";function ah(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return ij.has(t)?"image":cj.has(t)?"doc":null}function lj(e){let r=Li.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 BT(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>PT)throw new Error(`file too large \u2014 max ${Math.floor(PT/(1024*1024))} MB`);let r=lj(t),o=Li.extname(r).slice(1),s=ah(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=aj().slice(0,8),i=sh(e);await oh.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await oh.writeFile(Li.join(i,c),n,{mode:384}),await oh.chmod(Li.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${DT}/${c}`,kind:s,size:n.length}}function jT(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function ih(e){return e.role==="user"&&uj.test((e.text||"").trim())}var DT,sh,PT,ij,cj,uj,ch=L(()=>{DT=".bostrat/uploads",sh=e=>Li.join(e,DT),PT=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,ij=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),cj=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"]);uj=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Oo(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var LT=L(()=>{});import{execFile as dj}from"node:child_process";import{promisify as fj}from"node:util";function lh(e){return`refs/bostrat/wip/${e}`}function hj(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function HT(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function wj(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:mj}async function uh(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await jr(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await jr(o,["remote","get-url","origin"])}catch{return FT.has(o)||(FT.add(o),t.log?.(`[wip-sync] ${r}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let s=await As(o,"bostrat: wip snapshot"),{stdout:a}=await jr(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&Iu.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=lh(r);return await jr(o,["push","--force","origin",`${s}:${c}`],UT),Iu.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 dh(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await jr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await jr(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await As(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await jr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Iu.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=hj(n);return await jr(r,["push","origin",`${o}:${i}`],UT),Iu.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 WT({listTracks:e,sweepMs:t=wj(),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 uh(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 pj,mj,UT,jr,Iu,FT,fh=L(()=>{Nl();de();Dn();pj=fj(dj),mj=12e4,UT=6e4;jr=(e,t,n=15e3)=>pj("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Iu=new Map,FT=new Set});import gj from"node:fs";import ph from"node:fs/promises";import yj from"node:path";function vj(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function qT(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(!vj(t))return{restored:!1,reason:"plane not configured"};let s=ts(e),a=yj.join(s,`${o}.ndjson`);try{if(gj.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=_e(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let d=new AbortController,p=setTimeout(()=>d.abort(),xj),h;try{h=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${bj}`,{headers:c,signal:d.signal})}finally{clearTimeout(p)}if(h.status===404)return{restored:!1,reason:"unknown to plane"};if(!h.ok)return{restored:!1,reason:`plane ${h.status}`};let w=await h.json();for(let g of w.events||[])l.push(JSON.stringify(g));if(l.length>kj)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 ph.mkdir(s,{recursive:!0});let f=`${a}.pull-${process.pid}-${Date.now()}`;return await ph.writeFile(f,l.join(`
|
|
202
206
|
`)+`
|
|
203
|
-
`),await
|
|
207
|
+
`),await ph.rename(f,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var bj,kj,xj,KT=L(()=>{it();de();bj=1e3,kj=5e5,xj=3e4});var SE={};Pd(SE,{HANDOFF_LATEST_REL:()=>Co,KNOWN_AGENTS:()=>Ji,TRACK_NAME_MAX:()=>Ah,addTrackRepo:()=>Hh,adoptTrack:()=>aw,agentAvailability:()=>Cu,announceNodeUpdate:()=>Sn,answerFullscreenOffer:()=>Qh,answerPrompt:()=>Nu,archiveTrack:()=>Do,archiveTrackForMergedPr:()=>Wn,augmentFirstPromptAfterHandoff:()=>tE,buildAgentCmd:()=>$h,canResumeSubtask:()=>kE,cancelQueued:()=>Qi,cancelQueuedToDraft:()=>Ph,cancelStopJob:()=>ec,cancelSubtask:()=>qu,changedRepos:()=>_o,commitTransferOut:()=>Hu,createSetupTrack:()=>Kh,createTrack:()=>Bu,deleteTrack:()=>Vh,demoteTransferredTracks:()=>sw,ensureLeadEpochs:()=>Qs,forceMergeSubtask:()=>uw,getHandoffJob:()=>ra,getSubtaskJob:()=>TL,getTrack:()=>te,getTransferOutJob:()=>nw,healDeadLead:()=>zi,interruptLead:()=>Du,isArchiving:()=>mL,isHandoffNoteEcho:()=>vh,isInterruptEcho:()=>Sh,isModelSwitchEcho:()=>Th,leadCliDead:()=>Yh,listSubtaskJobs:()=>jo,listTracks:()=>ce,matchTranscriptsToBoxes:()=>oE,mergeNotifyMessage:()=>wE,navigatePrompt:()=>Nh,notifyTrackForMergedPr:()=>qn,notifyTrackForResolvedPr:()=>ju,offerFollowupsForMergedPr:()=>Bo,pendingOffers:()=>Ch,queueShipCompleteNotify:()=>Bh,queuedMessages:()=>Mo,reapArchivedOrphans:()=>Zh,reconcileDemotedTracks:()=>Xh,reconcileHandoffOutcome:()=>gE,reconcileInterruptedHandoffs:()=>tw,reconcileInterruptedSubtasks:()=>iw,reconcileNodeUpdateNotices:()=>Oh,reconcileSubtaskOutcome:()=>bE,refreshStandingInstructions:()=>qh,refuseIfTransferred:()=>ow,rehydrateLead:()=>nc,removeTrackRepo:()=>Wh,renameTrack:()=>Jh,reposOf:()=>Mt,resolveTrackBox:()=>_i,resolveTrackBoxByHookToken:()=>Zi,restartLead:()=>Fu,resumeAgentCmd:()=>Rh,sayToLead:()=>Ct,sendOptionsForAgent:()=>un,setTrackDraft:()=>Fh,setTrackModel:()=>Uh,startLeadHandoff:()=>Uu,startSpinUpView:()=>jh,startSubtask:()=>lw,startTransferOut:()=>rw,stopJobActive:()=>iE,subtaskMessages:()=>Mh,trackMessages:()=>Mu,unarchiveTrack:()=>Lu});import V from"node:fs/promises";import Y from"node:path";import{randomUUID as Po}from"node:crypto";import{execFile as Sj}from"node:child_process";import{promisify as Tj}from"node:util";function $h(e,{skipPermissions:t=!0}={}){let n=Ou[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 Rh(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Ou[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&&!Vt(r,t)){let c=o&&Ne(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 na(e){let t=Ou[e];if(t)return process.env[t.envVar]||t.base}async function Cu({maxAgeMs:e=6e4}={}){if(Lr.value&&Date.now()-Lr.at<e)return Lr.value;let t=Lr.value,n=await Promise.all(Ji.map(async r=>{let o=await ol(na(r),{retries:[]});return o===is?[r,t?.[r]??!0]:[r,o===rl]}));return Lr.value=Object.fromEntries(n),Lr.at=Date.now(),Lr.value}async function Xi(e,t=!0){let n=$h(e,{skipPermissions:t});if(await ol(n)===as){Lr.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.":pf[r]?`Install it on this node: ${pf[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 tE(e,t=Co){return`${e.trim()}
|
|
204
208
|
|
|
205
|
-
(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
|
|
206
|
-
`;return Qm=Qm.then(()=>K.mkdir(yn,{recursive:!0})).then(()=>K.appendFile(MB,n)).catch(()=>{}),Qm}async function bn({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 K.readFile(eh,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await X(),u=0;for(let d of l.tracks)qe(d)||!d.leadBox||(ve(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 Q().catch(()=>{}),s==="available")try{await K.mkdir(J.dirname(eh),{recursive:!0}),await K.writeFile(eh,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function hh({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await X(),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)||LB(e,c)||(ve(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 Q().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function LB(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 ST(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=>FB.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 TT(e,t,n,{kind:r}={}){let o=await fe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Js(o.agent);await ur({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Je(t,n,{...sn(o.agent,n),kind:r||"orchestration"});let a=await Ya({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await ee({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await X(),c=le(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);ve(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await Q()}async function UB(e){let t=J.join(Ko(e),`${e.id}.ndjson`),n;try{n=await K.stat(t)}catch{return null}let r=pT.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await F(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return pT.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function HB(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Jp(n.name,n.agent)}async function Dt(e,t=null){let n=t??new Map((await Ut()).map(c=>[c.name,c])),r=!!e.transferredTo,o=e.boxes.map(c=>{let l=r?null:n.get(c.name),u=Mr.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([UB(e),HB(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:Fi.has(e.id)}}function AT(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-WB,f=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function ET(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=Fy(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=AT(u,a,f=>i.has(f));for(let f of d){let p=u.find(m=>m.name===f.box);p&&(p.sessionId=f.sessionId),await Oy(f.box,f.sessionId),n=!0}}return n}async function ie(){let e=await X();await ET(e)&&await Q();let t=new Map((await Ut()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Dt(n,t)))}async function ki(e,t){let n=await X();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 Ki(e){let t=wa(e);if(!t)return null;let n=await X();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&bg(o.hookTokenHash,t))return{track:r,box:o};return null}async function oe(e){let t=await X(),n=le(t,e);return n?(await ET(t)&&await Q(),Dt(n)):null}async function wh(e){let t=await F(e).catch(()=>[]),n;try{n=dr(t)}catch{return[]}return Jb(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function ih(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(qB[e.agent]||[]).some(n=>n.test(t))}function ch(e){let t=(e.text||"").trim();return t?KB.some(n=>n.test(t)):!1}async function Su(e,t={}){let n=await oe(e);if(!n)return null;let r=new Set([...n.events.filter(g=>g.type==="orchestration-prompt"&&g.prompt).map(g=>String(g.prompt).trim()),...n.events.filter(g=>g.type==="said"&&g.text).map(g=>String(g.text).trim())]),o=Ys(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=Sm(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Jv(n),...l],f={windows:[...yk(n),...bk(n,kl),...a,...u]},m=(await Promise.all(n.boxes.map(g=>lp(g,t)))).flat(),w=m.map(g=>{let _={kind:"message",...g};if(_.role==="assistant"&&(_.text||"").trim()&&_.tools?.length){let ne=ri(_.agent).cliTools,ae=_.tools.filter(Te=>!ne.has(Te));return{..._,tools:ae.length?ae:void 0}}return _}).map(g=>{if(g.role!=="user")return g;let _=gl(g.text);return _?_.kind==="suppress"?null:{...g,note:_,text:Fb(_)}:g}).filter(Boolean).filter(g=>!(g.role==="user"&&r.has((g.text||"").trim()))).filter(g=>!ih(g)).filter(g=>!ch(g)).filter(g=>!ah(g)).filter(g=>!Km(g)).filter(g=>g.role!=="assistant"||!!(g.text||"").trim()||!!g.images?.length).filter(g=>!kk(g,f)),y=w.filter(g=>g.failed&&g.text===Qr);if(y.length){let g=new Map;for(let _ of y){if(!g.has(_.box)){let Te=[...(await Oe(_.box,{lines:ze}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Ee=>Ee[1]);g.set(_.box,Te.length?Te[Te.length-1]:null)}let ne=g.get(_.box);ne&&(_.text=`\u25A0 ${ne}`)}}let x=await Promise.all(n.boxes.map(g=>Qk(g,t)));await xv(n,x.flat());let{agentTurnBoxes:T}=await bv(n,w);for(let g of n.boxes)T.has(g.name)&&g.cwd&&Fc(g.cwd);await z0(n),await nS(n,w).catch(()=>{});for(let g of n.boxes)g.agent==="codex"&&g.state==="live"&&await Sv(n,g),g.agent==="cursor"&&g.state==="live"&&await Iv(n,g);let I=await Promise.all(n.boxes.map(g=>Zk(g,t)));await kv(n,I.flat().filter(g=>!th(g.box,g.ts,u)));let $=await F(n),D=c&&i.viewId?i.viewId:null,S=ax($),A=dr($).filter(g=>!(g.kind==="message"&&(ih(g)||ch(g)||ah(g)||Km(g)))&&!(g.kind==="view-card"&&D&&g.viewId===D)),R=ix(A,S),E=A.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&!R.has(g)&&th(g.box,g.ts,S))),L=Wf($,E).map(g=>({kind:"command-batch",id:g.id,ts:g.pending?g.ts:(g.anchorTs??g.ts)-1,anchorTs:g.anchorTs,anchorBox:g.anchorBox,agent:g.agent,boxRole:g.boxRole,commands:g.commands,pending:g.pending})).filter(g=>!th(g.anchorBox,g.anchorTs??g.ts,u)),U=Vb(zb([...E,...L].filter(g=>g.ts!=null).sort((g,_)=>g.ts-_.ts))),se=VB(n),G=se.size?U.filter(g=>g.kind==="event"||!se.has(g.box??g.anchorBox)):U,C=n.boxes.find(g=>g.name===n.leadBox)||null,Z=C?await La(C.cwd,C.sessionId,{fallbackNewest:!1}):null;if(!Z&&C){let g=[...$].reverse().find(_=>_.type==="context.usage"&&_.boxId===C.name);g?.payload?.window&&(Z={pct:g.payload.pct??(g.payload.occupancy!=null?g.payload.occupancy/g.payload.window:null),occupancy:g.payload.occupancy??null,window:g.payload.window,windowKnown:!0,model:g.payload.model||null})}let Y=[...m].reverse().find(g=>g.role==="assistant"&&g.model),te=Y?.model||null,we=[...n.events||[]].reverse().find(g=>g.type==="model-change"),ce=Z?.model||te;we&&(!Y?.ts||we.at>=Y.ts)&&(ce=we.to);let V=C?await Oe(C.name,{lines:ze}).catch(()=>""):"",ge=C&&co(C.agent,V)?await $n(C.name).catch(()=>V):V,B=C?br(C,ge):null;if(B&&C){let g=Al(C,B,ge);if(g){let _=h0(C.name,B.promptId);_.action!=="give-up"&&(_.action==="send"&&(De(C.name,g.keys,{kind:"auto-reject"}).catch(()=>{}),_.first&&O(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{ts:Date.now(),promptId:B.promptId,rule:g.id}}).catch(()=>{})),B=null)}}!B&&C&&!as(C.agent,V)&&!vo(n.id).length&&(B=Uf(G,{leadBoxId:C.name,provider:C.agent,isAnswered:g=>$.some(_=>_.type==="prompt.answered"&&_.payload?.promptId===g)})),B&&J0(n,C,B);let ye=G.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&g.ask&&!String(g.text||"").trim()&&!g.images?.length)),_e=!!C&&Yl(C.name,C.agent,V)&&!B;C&&!C.readyAt&&(_e||B||V&&is(C.agent,V))&&(C.readyAt=Date.now(),await Q());let Ie=!!C&&!C.readyAt&&Date.now()-(n.createdAt||0)<GB;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&C&&is(C.agent,V)&&!B){let g=await X(),_=le(g,n.id);_?.setup&&!_.setup.connectedAt&&(_.setup.connectedAt=Date.now(),n.setup.connectedAt=_.setup.connectedAt,await O(_,{type:"provider.connected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{provider:C.agent,ts:_.setup.connectedAt}}).catch(()=>{}),await Q())}let b=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,h=null;if(c){let g=Date.now()-i.startedAt;if(i.viewId&&!_e){let _=iu(n.id);_&&(ve(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:_.finishedAt}),await Q())}else if(B||g>Vv&&!_e&&!i.viewId||g>vm){let _=B?"needs-input":g>vm?"timeout":"no-view";iu(n.id,{error:_})&&await O(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:C?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:_}}).catch(()=>{})}else h={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ye,context:Z?{pct:Z.pct,occupancy:Z.occupancy,window:Z.window,windowKnown:Z.windowKnown,model:Z.model}:null,models:{current:ce,options:U0(C?.agent)},pendingPrompt:B,working:_e,stopping:IT(n.id),leadStarting:Ie,handoff:b,view:h,queued:vo(n.id).filter(g=>g.kind!=="ship-notify"&&g.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function VB(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 gh(e,t){let n=await oe(e);if(!n)return null;let r=Eo(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 F(n),a=dr(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Wf(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 Oe(o,{lines:ze}).catch(()=>"");u=Yl(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Tu(e,{promptId:t,answers:n,text:r}={}){let o=le(await X(),e);if(!o)throw new Error(`track "${e}" not found`);Vi(e,"prompt-answer");let s=o.boxes.find(m=>m.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await fe(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await wp(s);if(i||(i=await zB(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let m=typeof r=="string"?r.trim():"";if(!m)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(m)||m.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Je(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await De(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let m=qb(i,n||[],typeof r=="string"?r:"");if(!m)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Bt(o.id,m,{kind:"prompt-answer"}),c={mode:"say",value:m,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let m=d0(i,n||[]);await De(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=mb(i,n||[]);await Je(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:m,count:1}}if(i.answerMode==="say"){let m=i.questions||[];for(let w=0;w<m.length;w++){let y=m[w],x=new Set(Array.isArray(n?.[w])?n[w]:[]),T=[...x].map($=>y.options?.[$]).filter(Boolean).map($=>({label:$.label,description:$.description||""})),I=(y.options||[]).map(($,D)=>x.has(D)?null:$?.label).filter(Boolean);Mp(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??m.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:T,rejected:I})}return await ee({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),d=[...u].map(m=>l.options?.[m]).filter(Boolean).map(m=>({label:m.label,description:m.description||""})),f=(l.options||[]).map((m,w)=>u.has(w)?null:m?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:d,rejected:f,...p?{control:p}:{}},await ee({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Mp(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function zB(e,t){if(!t)return null;let n=await F(e).catch(()=>[]);return Uf(dr(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function yh(e,{promptId:t,questionIndex:n}={}){let r=le(await X(),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 fe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await wp(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=f0(a,n);return c.length?(await De(r.leadBox,c,{kind:"navigate-prompt"}),await ee({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 Au(e){let t=Di.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Di.set(e,t)),t}function vo(e){return(Di.get(e)?.items??[]).map(t=>({...t}))}function Gi(e){let t=Di.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function bh(e){let t=Gi(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=le(await X(),e);if(o)return r=Ks(o,n),await Q(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function kh(e){let t=Au(e);t.timer||(t.timer=setInterval(()=>YB(e).catch(()=>{}),JB))}async function YB(e){let t=Di.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=le(await X(),e);if(!n||n.archivedAt){Gi(e);return}let r=Ys(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){ko(t,!1);return}let o=await fe(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await Ch(o)&&(o=await Ui(n,s,"queue")?await fe(n.leadBox):null,await Q()),!o){ko(t,!1);return}if(uo(s?.agent)){ko(t,!1);return}let a=await Oe(n.leadBox,{lines:ze}).catch(()=>"");if(ll(s?.agent,a)){ko(t,!1);return}if(co(s?.agent,a)){let c=await $n(n.leadBox).catch(()=>a);if(br(s,c)){ko(t,!1);return}}if(!ko(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await lh(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 Ui(n,s,"queue-gate")}await Q()}finally{t.flushing=!1}}}async function _T(e,t,n,r){let o=uo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Ks(e,[n]),await Q()),O0(e.id,o))try{await O(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 XB(e,t){let n=await Oe(e.leadBox,{lines:ze}).catch(()=>"");if(!co(t?.agent,n))return!1;let r=await $n(e.leadBox).catch(()=>n);return!!br(t,r)}async function lh(e,t,n,r,o=null,s=null){await _T(e,t,r,o);let a=Bi.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=vT(r,e.pendingHandoffHydrate.artifact||xo),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(u.length){let f=sT(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 vh(e){return e.role==="user"&&_j.test((e.text||"").trim())}async function Z(){return Ut||(mh||=(async()=>{try{Ut=JSON.parse(await V.readFile(QT,"utf8"))}catch{Ut={tracks:[]}}finally{mh=null}return Ut})(),mh)}async function ee(){await vt(QT,Ut)}function Rj(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
210
|
+
`;return hh=hh.then(()=>V.mkdir(vn,{recursive:!0})).then(()=>V.appendFile(Ej,n)).catch(()=>{}),hh}async function Sn({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 V.readFile(wh,"utf8"))}catch{}if(f?.latest===t&&f?.current===e)return{announced:0,deduped:!0}}let l=await Z(),u=0;for(let f of l.tracks)Ge(f)||!f.leadBox||(Te(f,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ee().catch(()=>{}),s==="available")try{await V.mkdir(Y.dirname(wh),{recursive:!0}),await V.writeFile(wh,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Oh({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await Z(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||Oj(e,c)||(Te(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ee().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function Oj(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 nE(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=>Cj.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 rE(e,t,n,{kind:r}={}){let o=await ge(t);if(!o)throw new Error(`box "${t}" is not running`);let s=na(o.agent);await hr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Qe(t,n,{...un(o.agent,n),kind:r||"orchestration"});let a=await ti({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await re({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await Z(),c=pe(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);Te(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await ee()}async function Mj(e){let t=Y.join(ts(e),`${e.id}.ndjson`),n;try{n=await V.stat(t)}catch{return null}let r=GT.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await F(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return GT.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Nj(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:am(n.name,n.agent)}async function Rt(e,t=null){let n=t??new Map((await Kt()).map(c=>[c.name,c])),r=!!e.transferredTo,o=e.boxes.map(c=>{let l=r?null:n.get(c.name),u=Fr.has(c.state)?c.state:l?c.role==="lead"?"live":c.state:"stopped";return{...c,state:u,cwd:l?.cwd||c.cwd,agent:l?.agent||c.agent,sessionId:l?.sessionId||c.sessionId}}),[s,{working:a,prompted:i}]=await Promise.all([Mj(e),Nj(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:Vi.has(e.id)}}function oE(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-Pj,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 sE(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=rb(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=oE(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 zy(d.box,d.sessionId),n=!0}}return n}async function ce(){let e=await Z();await sE(e)&&await ee();let t=new Map((await Kt()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Rt(n,t)))}async function _i(e,t){let n=await Z();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function Zi(e){let t=xa(e);if(!t)return null;let n=await Z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Dg(o.hookTokenHash,t))return{track:r,box:o};return null}async function te(e){let t=await Z(),n=pe(t,e);return n?(await sE(t)&&await ee(),Rt(n)):null}async function Ch(e){let t=await F(e).catch(()=>[]),n;try{n=wr(t)}catch{return[]}return fk(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function Sh(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(Dj[e.agent]||[]).some(n=>n.test(t))}function Th(e){let t=(e.text||"").trim();return t?Bj.some(n=>n.test(t)):!1}async function Mu(e,t={}){let n=await te(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=ra(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=Lm(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...IS(n),...l],d={windows:[...Pk(n),...Dk(n,_l),...a,...u]},h=(await Promise.all(n.boxes.map(E=>bp(E,t)))).flat(),w=h.map(E=>{let ne={kind:"message",...E};if(ne.role==="assistant"&&(ne.text||"").trim()&&ne.tools?.length){let Ke=ci(ne.agent).cliTools,y=ne.tools.filter(m=>!Ke.has(m));return{...ne,tools:y.length?y:void 0}}return ne}).map(E=>{if(E.role!=="user")return E;let ne=Tl(E.text);return ne?ne.kind==="suppress"?null:{...E,note:ne,text:rk(ne)}:E}).filter(Boolean).filter(E=>!(E.role==="user"&&r.has((E.text||"").trim()))).filter(E=>!Sh(E)).filter(E=>!Th(E)).filter(E=>!vh(E)).filter(E=>!ih(E)).filter(E=>E.role!=="assistant"||!!(E.text||"").trim()||!!E.images?.length).filter(E=>!Bk(E,d)),g=w.filter(E=>E.failed&&E.text===ao);if(g.length){let E=new Map;for(let ne of g){if(!E.has(ne.box)){let m=[...(await Oe(ne.box,{lines:Ze}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(b=>b[1]);E.set(ne.box,m.length?m[m.length-1]:null)}let Ke=E.get(ne.box);Ke&&(ne.text=`\u25A0 ${Ke}`)}}let k=await Promise.all(n.boxes.map(E=>w0(E,t)));await eS(n,k.flat());let{agentTurnBoxes:S}=await Zv(n,w);for(let E of n.boxes)S.has(E.name)&&E.cwd&&Vc(E.cwd);await dx(n),await PS(n,w).catch(()=>{});for(let E of n.boxes)E.agent==="codex"&&E.state==="live"&&await nS(n,E),E.agent==="cursor"&&E.state==="live"&&await iS(n,E);let A=await Promise.all(n.boxes.map(E=>h0(E,t)));await Qv(n,A.flat().filter(E=>!gh(E.box,E.ts,u)));let R=await F(n),B=c&&i.viewId?i.viewId:null,T=Sx(R),_=wr(R).filter(E=>!(E.kind==="message"&&(Sh(E)||Th(E)||vh(E)||ih(E)))&&!(E.kind==="view-card"&&B&&E.viewId===B)),O=Tx(_,T),I=_.filter(E=>!(E.kind==="message"&&E.role==="assistant"&&!O.has(E)&&gh(E.box,E.ts,T))),U=Qf(R,I).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=>!gh(E.anchorBox,E.anchorTs??E.ts,u)),H=uk(dk([...I,...U].filter(E=>E.ts!=null).sort((E,ne)=>E.ts-ne.ts))),ae=Lj(n),J=ae.size?H.filter(E=>E.kind==="event"||!ae.has(E.box??E.anchorBox)):H,C=n.boxes.find(E=>E.name===n.leadBox)||null,Q=C?await qa(C.cwd,C.sessionId,{fallbackNewest:!1}):null;if(!Q&&C){let E=[...R].reverse().find(ne=>ne.type==="context.usage"&&ne.boxId===C.name);E?.payload?.window&&(Q={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 ve=[...h].reverse().find(E=>E.role==="assistant"&&E.model),W=ve?.model||null,be=[...n.events||[]].reverse().find(E=>E.type==="model-change"),se=Q?.model||W;be&&(!ve?.ts||be.at>=ve.ts)&&(se=be.to);let ue=C?await Oe(C.name,{lines:Ze}).catch(()=>""):"",me=C&&wo(C.agent,ue)?await Mn(C.name).catch(()=>ue):ue,j=C?Tr(C,me):null;if(j&&C){let E=Cl(C,j,me);if(E){let ne=C0(C.name,j.promptId);ne.action!=="give-up"&&(ne.action==="send"&&(Be(C.name,E.keys,{kind:"auto-reject"}).catch(()=>{}),ne.first&&$(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{ts:Date.now(),promptId:j.promptId,rule:E.id}}).catch(()=>{})),j=null)}}!j&&C&&!ws(C.agent,ue)&&!Mo(n.id).length&&(j=Xf(J,{leadBoxId:C.name,provider:C.agent,isAnswered:E=>R.some(ne=>ne.type==="prompt.answered"&&ne.payload?.promptId===E)})),j&&fx(n,C,j);let ie=J.filter(E=>!(E.kind==="message"&&E.role==="assistant"&&E.ask&&!String(E.text||"").trim()&&!E.images?.length)),Ae=!!C&&ou(C.name,C.agent,ue)&&!j;C&&!C.readyAt&&(Ae||j||ue&&gs(C.agent,ue))&&(C.readyAt=Date.now(),await ee());let $e=!!C&&!C.readyAt&&Date.now()-(n.createdAt||0)<jj;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&C&&gs(C.agent,ue)&&!j){let E=await Z(),ne=pe(E,n.id);ne?.setup&&!ne.setup.connectedAt&&(ne.setup.connectedAt=Date.now(),n.setup.connectedAt=ne.setup.connectedAt,await $(ne,{type:"provider.connected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{provider:C.agent,ts:ne.setup.connectedAt}}).catch(()=>{}),await ee())}let He=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,ze=null;if(c){let E=Date.now()-i.startedAt;if(i.viewId&&!Ae){let ne=gu(n.id);ne&&(Te(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:ne.finishedAt}),await ee())}else if(j||E>AS&&!Ae&&!i.viewId||E>jm){let ne=j?"needs-input":E>jm?"timeout":"no-view";gu(n.id,{error:ne})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:C?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:ne}}).catch(()=>{})}else ze={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ie,context:Q?{pct:Q.pct,occupancy:Q.occupancy,window:Q.window,windowKnown:Q.windowKnown,model:Q.model}:null,models:{current:se,options:ox(C?.agent)},pendingPrompt:j,working:Ae,stopping:iE(n.id),leadStarting:$e,handoff:He,view:ze,queued:Mo(n.id).filter(E=>E.kind!=="ship-notify"&&E.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function Lj(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 Mh(e,t){let n=await te(e);if(!n)return null;let r=jo(e).find(f=>f.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await F(n),a=wr(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Qf(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 Oe(o,{lines:Ze}).catch(()=>"");u=ou(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Nu(e,{promptId:t,answers:n,text:r}={}){let o=pe(await Z(),e);if(!o)throw new Error(`track "${e}" not found`);ec(e,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await ge(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Ap(s);if(i||(i=await Fj(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 Qe(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 Be(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=ik(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Ct(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=I0(i,n||[]);await Be(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=Ob(i,n||[]);await Qe(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let g=h[w],k=new Set(Array.isArray(n?.[w])?n[w]:[]),S=[...k].map(R=>g.options?.[R]).filter(Boolean).map(R=>({label:R.label,description:R.description||""})),A=(g.options||[]).map((R,B)=>k.has(B)?null:R?.label).filter(Boolean);Wp(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:S,rejected:A})}return await re({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),d=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:f,rejected:d,...p?{control:p}:{}},await re({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Wp(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function Fj(e,t){if(!t)return null;let n=await F(e).catch(()=>[]);return Xf(wr(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Nh(e,{promptId:t,questionIndex:n}={}){let r=pe(await Z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await ge(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Ap(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=$0(a,n);return c.length?(await Be(r.leadBox,c,{kind:"navigate-prompt"}),await re({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Pu(e){let t=Wi.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Wi.set(e,t)),t}function Mo(e){return(Wi.get(e)?.items??[]).map(t=>({...t}))}function Qi(e){let t=Wi.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 Ph(e){let t=Qi(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=pe(await Z(),e);if(o)return r=Zs(o,n),await ee(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Dh(e){let t=Pu(e);t.timer||(t.timer=setInterval(()=>Hj(e).catch(()=>{}),Uj))}async function Hj(e){let t=Wi.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=pe(await Z(),e);if(!n||n.archivedAt){Qi(e);return}let r=ra(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Oo(t,!1);return}let o=await ge(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await Yh(o)&&(o=await zi(n,s,"queue")?await ge(n.leadBox):null,await ee()),!o){Oo(t,!1);return}if(yo(s?.agent)){Oo(t,!1);return}let a=await Oe(n.leadBox,{lines:Ze}).catch(()=>"");if(wl(s?.agent,a)){Oo(t,!1);return}if(wo(s?.agent,a)){let c=await Mn(n.leadBox).catch(()=>a);if(Tr(s,c)){Oo(t,!1);return}}if(!Oo(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await Eh(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 zi(n,s,"queue-gate")}await ee()}finally{t.flushing=!1}}}async function aE(e,t,n,r){let o=yo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Zs(e,[n]),await ee()),z0(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 Wj(e,t){let n=await Oe(e.leadBox,{lines:Ze}).catch(()=>"");if(!wo(t?.agent,n))return!1;let r=await Mn(e.leadBox).catch(()=>n);return!!Tr(t,r)}async function Eh(e,t,n,r,o=null,s=null){await aE(e,t,r,o);let a=qi.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=tE(r,e.pendingHandoffHydrate.artifact||Co),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=jT(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
207
211
|
|
|
208
|
-
${
|
|
212
|
+
${d}`:d}let f=Te(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||Xs(e,null);try{if(await hr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:na(n.agent),bailOnDialog:!0}),(qi.get(e.id)||0)!==a){!o&&r.trim()&&(Zs(e,[r]),await ee());let d=new Error("delivery stopped \u2014 the message was handed back as the draft");throw d.interrupted=!0,d}if(await Wj(e,t)){let d=Pu(e.id),p={id:Po(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};d.items.unshift(p),Dh(e.id);let h=new Error("lead is blocked on a prompt \u2014 message queued");throw h.requeued={id:p.id,position:d.items.length},h}await Qe(e.leadBox,i,{...un(t?.agent,i),kind:c?"lead-handoff-first-prompt":"say"})}catch(d){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),Te(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,...d?.interrupted?{reason:"interrupted"}:{},...d?.requeued?{reason:"requeued"}:{}}),d?.requeued)return{queued:!0,...d.requeued};throw d}await rL(e,t,i)||(await cE(e.leadBox,t?.agent,i.length+8,i).catch(()=>{}),Te(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,reason:"unsubmitted"}),o||Zs(e,[r])),c&&await re({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Bh(e,{actions:t}={}){Pu(e).items.push({id:Po(),text:Ex(t),ts:Date.now(),kind:"ship-notify"}),Dh(e)}async function Ct(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"||!Ts(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=pe(await Z(),e);if(!s)throw new Error(`track "${e}" not found`);if(s.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);ow(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=Ts(t);await aE(s,a,i,n),ec(e,"say");let c=await ge(s.leadBox);if(c&&await Yh(c)?await zi(s,a,"say")?(await ee(),c=await ge(s.leadBox)):c=null:!c&&await nc(s)&&(await ee(),c=await ge(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Pu(e),u=await Oe(s.leadBox,{lines:Ze}).catch(()=>""),f=l.items.length>0||wl(a?.agent,u);if(!f&&wo(a?.agent,u)){let p=await Mn(s.leadBox).catch(()=>u);f=!!Tr(a,p)}if(f){let p={id:Po(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),Dh(e),n||Xs(s,null),await ee(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let d;try{d=await Eh(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await zi(s,a,"say-gate")||(await ee(),c=await ge(s.leadBox),!c))throw p;d=await Eh(s,a,c,i,n,o)}return await ee(),d?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:d.position}:{ok:!0,leadBox:s.leadBox}}async function jh(e,{hint:t}={}){let n=pe(await Z(),e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it first`);let r=Lm(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";_S(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${VT}
|
|
209
213
|
|
|
210
|
-
What to preview: ${String(t).trim()}`:
|
|
214
|
+
What to preview: ${String(t).trim()}`:VT;try{await Ct(e,a,{kind:"spin-up-view"})}catch(i){throw gu(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Du(e){let t=pe(await Z(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);qi.set(e,(qi.get(e)||0)+1);let n=Qi(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Js.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Zs(t,r),await ee()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=Yj(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,No(qj)]);let a=pe(await Z(),e)||t;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}function iE(e){let t=Js.get(e);return!!t&&!t.canceled}function ec(e,t){let n=Js.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function Lh(e){ec(e,"track-ended"),qi.delete(e),Qi(e)}function Yj(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}),Js.set(e,r),(async()=>{let s=pe(await Z(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await ge(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<Kj&&!r.canceled;g++){await Be(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of Gj){await No(k);let S=await Oe(s.leadBox,{lines:Ze}).catch(()=>"");if(!ws(a?.agent,S)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<zj&&!r.canceled&&(await No(Vj),!r.canceled);g++){let k=await Oe(s.leadBox,{lines:Ze}).catch(()=>"");if(!ws(a?.agent,k)){c=!0;break}await Be(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=pe(await Z(),e)||s,f=u?.boxes.find(g=>g.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await Qj(u,f).catch(()=>null),d||(p=await nL(u,f).catch(()=>null)));let h=u&&i&&!r.canceled?await Xj(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=Zs(u,w),await ee()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await Jj(u||s,f,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!h,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await re({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!h}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),Js.get(e)===r&&Js.delete(e),o()}),r}async function Jj(e,t,n){await $(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function Xj(e){let t=xu(await F(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 Qj(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 Zj){await No(r);let o=await Oe(e.leadBox,{lines:Ze}).catch(()=>""),s=Qa(t.agent,o);if(s&&gl(n.text,s))return await cE(e.leadBox,t.agent,n.text.length+8),Te(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function nL(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>eL||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ue(r.text))return null;for(let s of tL){await No(s);let a;try{a=await bp(t,{})}catch{return null}if(Fb(a,r))return null;let i=await Oe(e.leadBox,{lines:Ze}).catch(()=>"");if(Qa(t.agent,i))return null}return Te(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function rL(e,t,n){return ti({sessionName:e.leadBox,agent:t?.agent,agentCmd:na(t?.agent),text:n,trackId:e.id,flow:"say"})}async function cE(e,t,n,r=null){for(let o=0;o<3;o++){await ls(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await No(200);let s=await Oe(e,{lines:Ze}).catch(()=>""),a=Qa(t,s);if(!a||r&&!gl(r,a)&&!(t==="codex"&&Ff(r,a)))return!0;n=a.length+8}return!1}function Xs(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 Zs(e,t){let n=[],r=o=>n.some(s=>Ue(s).includes(Ue(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?Xs(e,n.join(`
|
|
211
215
|
|
|
212
|
-
`),{source:"restore"})?.text??null:null}function
|
|
216
|
+
`),{source:"restore"})?.text??null:null}function sL(e,t){let n=Ue(t);if(!n)return!1;if(Mo(e.id).some(o=>Ue(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>oL)break;if(!(s.type!=="said"||!s.text||Ue(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Fh(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=pe(await Z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=Ts(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&sL(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;Ue(o).includes(Ue(i))?Xs(r,o):Xs(r,`${i}
|
|
213
217
|
|
|
214
|
-
${o}`,{source:"restore"})}else
|
|
215
|
-
`);return`${
|
|
218
|
+
${o}`,{source:"restore"})}else Xs(r,o);return await ee(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Uh(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=pe(await Z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await ge(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(f=>f.name===r.leadBox)?.agent||o.agent||"claude",i=sx(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await qa(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await hr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:na(o.agent)});let u=await ax(a,r.leadBox,i);return Te(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ee(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function lE(e){for(let t of e){if(!t||!await Es(t))continue;let n=await D0(t).catch(()=>null);if(!n)continue;if((await V.stat(Y.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Mt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:Y.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function _o(e){let t=[];for(let n of Mt(e?.workspace)){if(!n?.dir)continue;let r=await li(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await ui(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function uE(e){let t=e;typeof t=="string"&&!Y.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Zo(t)??await Lc(t));let n=await lE([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 aL(e,t,n,r){let o=`track/${cw(t)}-${e}`,s=[];try{await V.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await uE(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Se(c).catch(()=>null),u=Y.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${Y.basename(c)}-${p}`;a.add(u);let f=Y.join(n,u),d=await Is(c);await _s(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await tc(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 V.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Hh(e,t){let n=await Z(),r=pe(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 uE(typeof t=="string"?t.trim():t);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Se(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=Y.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${Y.basename(s)}-${d}`;let l=Y.join(o.dir,c),u=await Is(s);await B0(s,o.branch)?await Ml(s,l,o.branch):await _s(s,l,o.branch,u);let f={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),f],await tc(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"add",repo:a??s,name:c,workspace:dE(o)}),await ee(),f}async function Wh(e,t){let n=await Z(),r=pe(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 Ot.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 li(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await ui(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 V.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await tc(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:dE(o)}),await ee(),{removed:s.name}}async function iL(e,t,{repo:n,repos:r,cwd:o}={}){await V.mkdir(Ui,{recursive:!0});let s=Y.join(Ui,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return aL(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!Y.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Zo(n)??await Lc(n));let i=await lE([n,o,Aj]);if(i){let c=`track/${cw(t)}-${e}`;try{let l=await Is(i);await _s(i,s,c,l),await Gi(s);let u=await Se(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await V.mkdir(s,{recursive:!0}),await Mp(s),await Gi(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Gi(e){try{await V.writeFile(Y.join(e,"AGENTS.md"),Fi,{flag:"wx"})}catch{return}await dL(e,["/AGENTS.md"])}function uL(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`${lL}
|
|
216
220
|
|
|
217
221
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
218
222
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -224,20 +228,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
224
228
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
225
229
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
226
230
|
or preview an app, run it from that app's own repo directory.
|
|
227
|
-
`}async function
|
|
228
|
-
`).includes(s));o.length&&await
|
|
231
|
+
`}async function tc(e,t,n){let r=uL(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await V.writeFile(Y.join(e,o),r).catch(()=>{})}async function pE(e){let{stdout:t}=await ZT("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return Y.resolve(e,t.trim())}async function dL(e,t){try{let n=await pE(e);await V.mkdir(Y.dirname(n),{recursive:!0});let r=await V.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
232
|
+
`).includes(s));o.length&&await V.appendFile(n,(r.endsWith(`
|
|
229
233
|
`)||!r?"":`
|
|
230
234
|
`)+o.join(`
|
|
231
235
|
`)+`
|
|
232
|
-
`)}catch{}}async function
|
|
236
|
+
`)}catch{}}async function fL(e,t){try{let n=await pE(e),r=await V.readFile(n,"utf8"),o=r.split(`
|
|
233
237
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
234
|
-
`).length&&await
|
|
235
|
-
`))}catch{}}async function
|
|
238
|
+
`).length&&await V.writeFile(n,o.join(`
|
|
239
|
+
`))}catch{}}async function qh(){let e={refreshed:0,removed:0,codexGlobal:null};try{await V.mkdir(vn,{recursive:!0}),await V.writeFile(Y.join(vn,"CLAUDE.md"),cL).catch(()=>{}),await V.writeFile(Y.join(vn,"AGENTS.md"),Fi).catch(()=>{});let t=process.env.CODEX_HOME||Y.join(process.env.HOME||"/tmp",".codex");if((await V.stat(t).catch(()=>null))?.isDirectory()){let r=Y.join(t,"AGENTS.md"),o=await V.readFile(r,"utf8").catch(()=>null);o===null?(await V.writeFile(r,bh,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):yh(o)&&o!==bh&&(await V.writeFile(r,bh).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Ui,ta]){let o=await V.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=Y.join(r,s.name),i=Y.join(a,"AGENTS.md"),c=await V.readFile(i,"utf8").catch(()=>null);c!==null&&yh(c)&&c!==Fi&&(await V.writeFile(i,Fi).catch(()=>{}),e.refreshed++);let l=Y.join(a,"CLAUDE.md"),u=await V.readFile(l,"utf8").catch(()=>null);u!==null&&yh(u)&&(await V.rm(l,{force:!0}).catch(()=>{}),await fL(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function mE(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Mt(t))n.base&&n.dir&&await _t(n.base,n.dir).catch(()=>{});await V.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await _t(t.base,t.dir):await V.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function Bu({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 Xi(o,s),i=await Z(),c=Po().slice(0,8),l=await iL(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(h=>(h.boxes||[]).map(w=>w.name))),f=`${e.trim()} lead`;u.has(sl(f))&&(f=`${e.trim().slice(0,50).trim()} ${c} lead`);let d=await pr(f,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0}),p={id:c,name:e.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[ew(d,"lead","live")],events:[],createdAt:Date.now()};return Te(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:h,base:w,baseRef:g,repo:k})=>({name:h,base:w,baseRef:g,repo:k}))}:{}}}),i.tracks.push(p),await ee(),Ja({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a,onReady:async()=>{let h=p.boxes.find(w=>w.name===p.leadBox);!h||h.readyAt||(h.readyAt=Date.now(),await ee())}}),Rt(p)}async function Kh({provider:e="claude",fresh:t=!1}={}){if(!pL.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await Z()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Rt(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await Do(r.id).catch(()=>{})}let o=(await Xi(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Po().slice(0,8),a=Y.join(vn,"setup",s);await V.mkdir(a,{recursive:!0});let i=await pr(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&Ja({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:[ew(i,"lead","live")],events:[],createdAt:Date.now()};return Te(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await Z()).tracks.push(c),await ee(),Rt(c)}async function Gh(e,t,n){await Zd(t.cwd).catch(()=>{}),await NS(e).catch(()=>{});let r=ea.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),ea.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of jo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await dw(a).catch(()=>{}),Ot.delete(a.id);for(let a of Object.values(t.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await Z(),s=new Set((o.tracks||[]).filter(a=>a.id!==e&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of t.boxes)s.has(a.name)||await Gt(a.name).catch(()=>{})}async function Vh(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await Z(),o=r.tracks.findIndex(a=>a.id===e);if(o===-1)throw new Error(`track "${e}" not found`);let s=r.tracks[o];return t||await nT(s),Lh(e),await Gh(e,s,"track deleted"),n?await MT(e).catch(()=>{}):await CT(e).catch(()=>{}),await lx(),await mE(s),t&&await rT(e).catch(()=>{}),t?await kT(e).catch(()=>{}):await bT(s).catch(()=>{}),Te(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await ee(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Do(e){let t=await Z(),n=pe(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(),Lh(e),Te(n,"archived",{boxes:n.boxes.length}),await ee(),n.kind==="setup"?(await hE(e),Rt(n)):(Vi.add(e),wL(e).catch(()=>{}).finally(()=>Vi.delete(e)),{...await Rt(n),archiving:!0})}async function wL(e){try{await gL(e)}catch{}await hE(e).catch(()=>{})}async function hE(e){let t=await Z(),n=pe(t,e);if(!(!n||!n.archivedAt)){await Gh(e,n,"track archived");for(let r of n.boxes)Fr.has(r.state)||(r.state="stopped");await ee()}}async function gL(e){let t=await Z(),n=pe(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 ge(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Mk(n.leadBox,i.cwd,{timeoutMs:hL})),s="lead-cli"}catch{}if(r||(r=zk(n,await F(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 ee()}async function Wn(e,t){let n=await rt(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await zh(n);if(r.length){for(let o of r)await ko(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Do(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function zh(e){return(await xo().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 qn(e,t){return ju(e,t,"merged")}async function Bo(e,t){let n=await rt(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 zh(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await Dx(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=pe(await Z(),n.trackId);if(!a)return null;let i=s[0];return Ei(Rt(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 ju(e,t,n="merged"){let r=await rt(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 zh(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Px(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await Ct(r.trackId,wE(a),{kind:"merge-notify"}),!0}catch{return!1}}function wE(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 ${zT[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 ${zT[i.outcome]??i.outcome}`).join(`
|
|
236
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}
|
|
237
241
|
${s}
|
|
238
|
-
${a}`}async function Ch(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Ba()?!1:In(e.name)}async function Ui(e,t,n){return!t||e.archivedAt||Mr.has(t.state)?!1:(await ee({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 Ht(t.name).catch(()=>{}),t.state="stopped",Ji(e))}async function Ji(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=oh.get(r.name);if(o)return o;let s=Ej(e,r,{freshSession:t,reason:n}).finally(()=>oh.delete(r.name));return oh.set(r.name,s),s}async function Ej(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Mr.has(t.state)||await fe(t.name))return!1;let o=n===!0,s=o?To():null,a=o?Pa(ph(t.agent),s,t.name):mh(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Xc(a);if(i===Yo)return ve(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===Xo&&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(Gs(t)))}:null,l=Date.now();if(o){let u=Gs(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 cr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=wa(u.hookToken),Ka({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&ve(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),ve(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),ve(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Mh(e,{freshSession:t=!1}={}){let n=await X(),r=le(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,Fi.delete(e),ve(r,"unarchived",{}),await Ji(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await Q(),Dt(r)}async function Nh(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>uh)throw new Error(`name too long (max ${uh} chars)`);let r=await X(),o=le(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 Dt(o);let s=o.name;return o.name=n,ve(o,"renamed",{from:s,to:n}),await Q(),Dt(o)}async function Ph(){let e=await X(),t=0;for(let n of e.tracks||[])n.transferredTo&&Il(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await Q(),t}async function Dh(){let e=await X(),t=new Set((await Ut()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!qe(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(qe(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ht(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):ve(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Mr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Q(),r}async function $u(e,{freshSession:t=!1}={}){let n=await X(),r=le(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`);Hh(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Mr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await fe(r.leadBox)){let i=await Oe(r.leadBox,{lines:ze}).catch(()=>"");if(ll(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ht(r.leadBox),o.state="stopped"}let a=await Ji(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await Q(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Dt(r)}async function Bh(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 F(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await $u(e),await O(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 wT(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return ou({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function Gs(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 ku(e,t){let n=await X(),r=le(n,e);r&&(r.handoff={...r.handoff||{},...t},await Q())}function LT(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 Lh(){let e=await X(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=LT(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await fe(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=Gs(l),f=d[d.length-1],p=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:xo,at:u},ve(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await ee({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 ee({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ht(i).catch(()=>{}),a&&await Ht(a).catch(()=>{})}return t&&await Q(),t}async function Ru(e,t,n=!0,r=null){let o=Vs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=le(await X(),e);if(!s)throw new Error(`track "${e}" not found`);Vi(e,"handoff");let a=await qi(t,n),i=uo(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 fe(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:ST(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Vs.set(e,l),await ku(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),_j(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 ku(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ee({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ee({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function _j(e,t,n,r,o){let s=async(D,S={})=>{e.step=D,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${D}`),await ku(e.trackId,{step:D,toBox:e.toBox}),await ee({flow:"lead-handoff",phase:D,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:S.sessionName,...S})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await X(),i=le(a,e.trackId)?.boxes.find(D=>D.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",uo(c))e.checkpoint="skipped-capped";else try{await Gf(e.fromBox,{timeoutMs:xT})}catch(D){e.checkpoint="reconstructed",await ee({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:D?.message||String(D)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Vf(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 ep(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=kb(f.launchCmd);await ur({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Js(o),launchEcho:p}).catch(D=>{if(D?.code!=="cli-not-running")throw D});let m=_f(await Oe(e.fromBox,{lines:ze}).catch(()=>""),p);if(!is(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let D=mh(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await ep(e.fromBox,{agentCmd:D,agent:c,resumeSessionId:l,pinned:u});let S=await X(),A=le(S,e.trackId);if(A){let R=A.boxes.find(E=>E.name===e.fromBox);R&&(R.state="live"),ve(A,"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 Q()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await X(),y=le(w,e.trackId);if(!y)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let x=y.boxes.find(D=>D.name===e.fromBox);if(!x)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let T=Date.now(),I=Gs(x),$=I[I.length-1];$.endedAt=T,e.reason&&($.endReason=e.reason),I.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:T,endedAt:null}),x.agent=o,x.sessionId=f.sessionId,x.pinned=f.pinned,x.state="live",x.role="lead",y.pendingHandoffHydrate={artifact:e.statusArtifact||xo,at:T},ve(y,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:I.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await Q(),await s("done",{sessionName:e.fromBox}),await ku(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Uh(e){let t=fh.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await X(),r=le(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=Vs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of At.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 fh.set(e,s),Ij(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 Ij(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await fe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&uo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Gf(t.leadBox,{timeoutMs:xT})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Vf(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 Et(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Jt(u.dir,["push","origin",o],dh);let{stdout:f}=await Jt(u.dir,["rev-parse","HEAD"]);d.baseSha=f.trim(),d.pushed=!0}catch(f){d.error=String(f?.message||f).split(`
|
|
239
|
-
`)[0]}s.push(
|
|
240
|
-
`)[0]}else l?.sha&&(E="wip snapshots cover single-repo worktrees only");await dT({id:e,cwd:I}).catch(()=>({restored:!1}));let U=J.join(I,xo),se=null;(await qc(I)).restored?se="checkpoint":typeof u=="string"&&u.trim()&&(await K.mkdir(J.dirname(U),{recursive:!0}),await K.writeFile(U,u),se="brief");let C=new Set(x.tracks.flatMap(B=>(B.boxes||[]).map(ye=>ye.name))),Z=null;p&&!C.has(p)&&!await fe(p).catch(()=>null)?Z=p:(Z=`${t.trim()} lead`,C.has(Zc(Z))&&(Z=`${t.trim().slice(0,50).trim()} ${e} lead`));let Y=await cr(Z,I,{agentCmd:y,agent:n});Ka({cwd:Y.cwd,sessionName:Y.name,agent:n,agentCmd:y});let te=Date.now(),we=jh(Y,"lead","live"),ce=(Array.isArray(d)?d:[]).filter(B=>B&&typeof B=="object").map(B=>({...B})),V=ce[ce.length-1];V&&V.endedAt==null&&(V.endedAt=te,V.endReason==null&&(V.endReason=o==="recover"?"node-offline":"node-transfer")),ce.push({agent:n,sessionId:Y.sessionId??null,pinned:Y.pinned===!0,startedAt:te,endedAt:null}),we.epochs=ce;let ge={id:e,name:t.trim(),cwd:Y.cwd,workspace:$,leadBox:Y.name,boxes:[we],events:[],createdAt:Number.isFinite(f)?f:te,...se?{pendingHandoffHydrate:{artifact:xo,at:te}}:{}};return ve(ge,"transferred-in",{box:Y.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,...o==="recover"?{downSince:Number.isFinite(w)?w:null}:{},branch:$.branch??null,wip:R,...L?{wipAdvanced:L}:{},epoch:ce.length-1,name:ge.name,cwd:ge.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:B,base:ye,baseRef:be,repo:_e})=>({name:B,base:ye,baseRef:be,repo:_e}))}:{}}}),x.tracks.push(ge),await Q(),{...await Dt(ge),adopt:{wipApplied:R,wipSkipped:E,...L?{wipAdvanced:L}:{},codeless:D,artifactSource:se}}}catch(A){for(let R of S)await vt(R.base,R.dir).catch(()=>{});throw await K.rm(I,{recursive:!0,force:!0}).catch(()=>{}),A}}async function FT(e,t,n){let r=await X(),o=le(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await Q())}async function Hs(e,t){e.step=t,await Yi(e)}function UT(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 HT(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function Mj(e,t,n){if(!HT(n)||At.has(t)||!await fe(n.box).catch(()=>null))return!1;try{await K.access(n.dir)}catch{return!1}let o=await fe(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return At.set(t,s),qT(s,o).catch(a=>WT(s,a)),!0}async function Kh(){let e=await X(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(At.has(r))continue;if(await Mj(n,r,o)){t=!0,ve(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ee({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=UT(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Mr.has(i.state)&&(i.state="canceled"),ve(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 ee({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await yT(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Mr.has(r.state)||At.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?J.join(zs,`${n.id}-${r.subtaskId}`):null;await ee({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await yT(n,r.name,o)}}return t=await Nj(e)||t,t&&await Q(),t}async function yT(e,t,n){if(t&&await Ht(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Et(e.workspace)){let s=J.join(n,o.name);await vt(o.base,s).then(()=>!0,()=>!1)||await vt(o.dir,s).catch(()=>{})}await K.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await fe(e.leadBox).catch(()=>null);r?.cwd&&await vt(r.cwd,n).catch(()=>{})}async function Nj(e){let t;try{t=await K.readdir(zs)}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 At.values())o.dir&&n.add(J.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=J.join(zs,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Et(a.workspace))await vt(i.base,J.join(s,i.name)).catch(()=>{});await K.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await fe(a.leadBox).catch(()=>null):null;i?.cwd?await vt(i.cwd,s).catch(()=>{}):await K.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ee({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Vh(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 qi(t,r),a=uo(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=le(await X(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await fe(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=Et(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await hs(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await hs(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=To().slice(0,8),f={id:d,trackId:e,trackName:i.name,agent:t,task:ms(n),skipPermissions:r,reason:ST(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Gh(i.name)}-${d}`,base:null,repos:u,dir:J.join(zs,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return At.set(d,f),await Yi(f),Pj(f,c,s).catch(p=>WT(f,p)),f}async function WT(e,t){e.failedStep=e.step,e.error=t instanceof Hi?"canceled":t?.message||String(t),e.step=t instanceof Hi?"canceled":"error",e.finishedAt=Date.now(),await Jh(e).catch(()=>{}),await Yi(e).catch(()=>{}),await Lj(e).catch(()=>{})}function Mu(e){let t=At.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 zh(e){let t=At.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 Pj(e,t,n){if(await Hs(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await ws(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Yi(e)}else e.base=await ws(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Hs(e,"spawn"),await K.mkdir(zs,{recursive:!0}),e.repos){await K.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await gs(a.leadDir,J.join(e.dir,a.name),e.branch,a.base);let s=Et((le(await X(),e.trackId)||{}).workspace);await zi(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 gs(t.cwd,e.dir,e.branch,e.base),await Li(e.dir);await Dj(e,t);let r=e.trackName.slice(0,38).trim(),o=await cr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await Bj(e,o),await TT(e.trackId,o.name,DB(e.task,!!e.repos),{kind:"subtask"}),await qT(e,t)}async function qT(e,t){await Hs(e,"working");let n=J.join(e.dir,bu);if(await Uj(n,e),e.canceled)throw new Hi;if(await Hs(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=J.join(e.dir,l.name);await vp(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:wT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Ap(l.leadDir,l.base,e.branch,Us),l.files=await Ep(l.leadDir,l.base,e.branch,Us);let d=await _p(l.leadDir,l.base,e.branch,Us);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=>J.join(l.name,f))),i.push(...l.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${l.name}: ${f}`:J.join(l.name,f))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await vp(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:wT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Ap(t.cwd,e.base,e.branch,Us),e.files=await Ep(t.cwd,e.base,e.branch,Us);let s=await _p(t.cwd,e.base,e.branch,Us);e.applied=s.applied,e.conflicts=s.conflicts}await Yi(e);let r=await Fj(e,t,n);await Hs(e,"resume"),await TT(e.trackId,e.leadBox,BB(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Hs(e,"reap"),await Jh(e),e.step="done";let o=Date.now();await FT(e.trackId,e.id,{...Cu(e),finishedAt:o}),await jj(e,o),e.finishedAt=o}async function Dj(e,t){let n=J.join(e.dir,J.dirname(Pi));await K.mkdir(n,{recursive:!0});let r="";try{r=await rb(t.cwd,t.sessionId)}catch{}if(!r)try{r=await K.readFile(J.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
242
|
+
${a}`}async function Yh(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Ha()?!1:Cn(e.name)}async function zi(e,t,n){return!t||e.archivedAt||Fr.has(t.state)?!1:(await re({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Gt(t.name).catch(()=>{}),t.state="stopped",nc(e))}async function nc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=kh.get(r.name);if(o)return o;let s=yL(e,r,{freshSession:t,reason:n}).finally(()=>kh.delete(r.name));return kh.set(r.name,s),s}async function yL(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Fr.has(t.state)||await ge(t.name))return!1;let o=n===!0,s=o?Po():null,a=o?Fa($h(t.agent),s,t.name):Rh(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await ol(a);if(i===as)return Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===is&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(Qs(t)))}:null,l=Date.now();if(o){let u=Qs(t),f=u[u.length-1];f.endedAt==null&&(f.endedAt=l),f.endReason==null&&(f.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=await pr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=xa(u.hookToken),Ja({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&Te(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),Te(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Lu(e,{freshSession:t=!1}={}){let n=await Z(),r=pe(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,Vi.delete(e),Te(r,"unarchived",{}),await nc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ee(),Rt(r)}async function Jh(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Ah)throw new Error(`name too long (max ${Ah} chars)`);let r=await Z(),o=pe(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 Rt(o);let s=o.name;return o.name=n,Te(o,"renamed",{from:s,to:n}),await ee(),Rt(o)}async function Xh(){let e=await Z(),t=0;for(let n of e.tracks||[])n.transferredTo&&Pl(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ee(),t}async function Zh(){let e=await Z(),t=new Set((await Kt()).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 Gt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Te(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Fr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ee(),r}async function Fu(e,{freshSession:t=!1}={}){let n=await Z(),r=pe(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`);ow(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Fr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ge(r.leadBox)){let i=await Oe(r.leadBox,{lines:Ze}).catch(()=>"");if(wl(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Gt(r.leadBox),o.state="stopped"}let a=await nc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ee(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Rt(r)}async function Qh(e,t,n){let r=await te(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await F(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await Fu(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 YT(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return mu({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function Qs(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Ru(e,t){let n=await Z(),r=pe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ee())}function gE(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 Z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=gE(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ge(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),f=Qs(l),d=f[f.length-1],p=d.agent,h=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Co,at:u},Te(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await re({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await re({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Gt(i).catch(()=>{}),a&&await Gt(a).catch(()=>{})}return t&&await ee(),t}async function Uu(e,t,n=!0,r=null){let o=ea.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=pe(await Z(),e);if(!s)throw new Error(`track "${e}" not found`);ec(e,"handoff");let a=await Xi(t,n),i=yo(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await ge(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:nE(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ea.set(e,l),await Ru(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),bL(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 Ru(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),re({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),re({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function bL(e,t,n,r,o){let s=async(B,T={})=>{e.step=B,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${B}`),await Ru(e.trackId,{step:B,toBox:e.toBox}),await re({flow:"lead-handoff",phase:B,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:T.sessionName,...T})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await Z(),i=pe(a,e.trackId)?.boxes.find(B=>B.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",yo(c))e.checkpoint="skipped-capped";else try{await np(e.fromBox,{timeoutMs:eE})}catch(B){e.checkpoint="reconstructed",await re({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:B?.message||String(B)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await rp(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 up(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=Bb(d.launchCmd);await hr({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:na(o),launchEcho:p}).catch(B=>{if(B?.code!=="cli-not-running")throw B});let h=Bf(await Oe(e.fromBox,{lines:Ze}).catch(()=>""),p);if(!gs(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let B=Rh(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await up(e.fromBox,{agentCmd:B,agent:c,resumeSessionId:l,pinned:u});let T=await Z(),_=pe(T,e.trackId);if(_){let O=_.boxes.find(I=>I.name===e.fromBox);O&&(O.state="live"),Te(_,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await ee()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await Z(),g=pe(w,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let k=g.boxes.find(B=>B.name===e.fromBox);if(!k)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let S=Date.now(),A=Qs(k),R=A[A.length-1];R.endedAt=S,e.reason&&(R.endReason=e.reason),A.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:S,endedAt:null}),k.agent=o,k.sessionId=d.sessionId,k.pinned=d.pinned,k.state="live",k.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||Co,at:S},Te(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:A.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ee(),await s("done",{sessionName:e.fromBox}),await Ru(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function rw(e){let t=Ih.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await Z(),r=pe(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=ea.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Ot.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 Ih.set(e,s),kL(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 kL(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ge(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&yo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await np(t.leadBox,{timeoutMs:eE})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await rp(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 Mt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Qt(u.dir,["push","origin",o],_h);let{stdout:d}=await Qt(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
243
|
+
`)[0]}s.push(f)}e.step="wip";let a=await uh({id:t.id,cwd:t.cwd},{force:!0}),i=await Z(),c=pe(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Qs(l),await ee(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:f,baseSha:d})=>({name:u,repo:f,baseSha:d})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:f,error:d})=>({name:u,repo:f,error:d})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function ow(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 Hu(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await Z(),o=pe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Lh(e),await Gh(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Pl(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await dh({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,Te(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await ee(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function sw(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Hu(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 JT(e,t,n){try{await Ml(e,t,n)}catch{await Qt(e,["worktree","prune"]).catch(()=>{}),await Ml(e,t,n)}return t}async function aw(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(xh.has(t))throw new Error("an adopt is already running for this Track");xh.set(t,{startedAt:Date.now()});try{return await xL({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{xh.delete(t)}}async function xL({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:f=[],createdAt:d=null,leadBox:p=null,reason:h=null,downSince:w=null}){let g=await Xi(n,r),k=await Z(),S=pe(k,e);if(S&&!S.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);S&&(await dh({id:S.id,cwd:S.cwd}).catch(()=>{}),await mE(S),k.tracks.splice(k.tracks.indexOf(S),1),await ee()),await V.mkdir(Ui,{recursive:!0});let A=Y.join(Ui,e);await V.rm(A,{recursive:!0,force:!0}).catch(()=>{});let R=null,B=!1,T=[];try{let _=[];if(i)for(let j of Array.isArray(c)?c:[]){let ie=typeof j=="string"?j:j?.repo;if(!ie||!/^[^/\s]+\/[^/\s]+$/.test(ie))continue;let le=await Zo(ie)??await Lc(ie);await Qt(le,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],_h),_.push({slug:ie,base:le,name:typeof j=="object"?j?.name??null:null})}if(i&&_.length===1){let{base:j,slug:ie}=_[0];await JT(j,A,i),T.push({base:j,dir:A}),await Gi(A);let le=await Is(j);R={dir:A,base:j,branch:i,kind:"worktree",baseRef:le,repo:ie}}else if(i&&_.length>=2){await V.mkdir(A,{recursive:!0});let j=new Set([".bostrat"]),ie=[];for(let le of _){let Ae=le.name||Y.basename(le.base);j.has(Ae)&&(Ae=le.slug.replace("/","-"));for(let ze=2;j.has(Ae);ze++)Ae=`${Y.basename(le.base)}-${ze}`;j.add(Ae);let $e=Y.join(A,Ae);await JT(le.base,$e,i),T.push({base:le.base,dir:$e});let He=await Is(le.base);ie.push({name:Ae,dir:$e,base:le.base,baseRef:He,repo:le.slug})}await tc(A,ie,i),R={dir:A,kind:"multi",branch:i,repos:ie}}else B=!0,await V.mkdir(A,{recursive:!0}),await Mp(A),await Gi(A),R={dir:A,base:null,branch:null,kind:"repo"};let O=!1,I=null,U=null;if(l?.sha&&R.kind==="worktree")try{await Qt(A,["fetch","origin",l.ref||lh(e)],_h);let{stdout:j}=await Qt(A,["rev-parse","FETCH_HEAD^"]),{stdout:ie}=await Qt(A,["rev-parse","HEAD"]),le=j.trim()===ie.trim();le||await Qt(A,["merge-base","--is-ancestor",ie.trim(),j.trim()]).then(()=>!0,()=>!1)&&(await Qt(A,["reset","--hard",j.trim()]),U={from:ie.trim(),to:j.trim()},le=!0),le?(await Qt(A,["read-tree","--reset","-u","FETCH_HEAD"]),await Qt(A,["reset","-q"]),O=!0):I="snapshot does not parent the fetched branch head"}catch(j){I=String(j?.message||j).split(`
|
|
244
|
+
`)[0]}else l?.sha&&(I="wip snapshots cover single-repo worktrees only");await qT({id:e,cwd:A}).catch(()=>({restored:!1}));let H=Y.join(A,Co),ae=null;(await Xc(A)).restored?ae="checkpoint":typeof u=="string"&&u.trim()&&(await V.mkdir(Y.dirname(H),{recursive:!0}),await V.writeFile(H,u),ae="brief");let C=new Set(k.tracks.flatMap(j=>(j.boxes||[]).map(ie=>ie.name))),Q=null;p&&!C.has(p)&&!await ge(p).catch(()=>null)?Q=p:(Q=`${t.trim()} lead`,C.has(sl(Q))&&(Q=`${t.trim().slice(0,50).trim()} ${e} lead`));let ve=await pr(Q,A,{agentCmd:g,agent:n});Ja({cwd:ve.cwd,sessionName:ve.name,agent:n,agentCmd:g});let W=Date.now(),be=ew(ve,"lead","live"),se=(Array.isArray(f)?f:[]).filter(j=>j&&typeof j=="object").map(j=>({...j})),ue=se[se.length-1];ue&&ue.endedAt==null&&(ue.endedAt=W,ue.endReason==null&&(ue.endReason=o==="recover"?"node-offline":"node-transfer")),se.push({agent:n,sessionId:ve.sessionId??null,pinned:ve.pinned===!0,startedAt:W,endedAt:null}),be.epochs=se;let me={id:e,name:t.trim(),cwd:ve.cwd,workspace:R,leadBox:ve.name,boxes:[be],events:[],createdAt:Number.isFinite(d)?d:W,...ae?{pendingHandoffHydrate:{artifact:Co,at:W}}:{}};return Te(me,"transferred-in",{box:ve.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(w)?w:null}:{},branch:R.branch??null,wip:O,...U?{wipAdvanced:U}:{},epoch:se.length-1,name:me.name,cwd:me.cwd,workspace:{base:R.base??null,branch:R.branch??null,kind:R.kind,...R.baseRef?{baseRef:R.baseRef}:{},...R.repo?{repo:R.repo}:{},...R.repos?{repos:R.repos.map(({name:j,base:ie,baseRef:le,repo:Ae})=>({name:j,base:ie,baseRef:le,repo:Ae}))}:{}}}),k.tracks.push(me),await ee(),{...await Rt(me),adopt:{wipApplied:O,wipSkipped:I,...U?{wipAdvanced:U}:{},codeless:B,artifactSource:ae}}}catch(_){for(let O of T)await _t(O.base,O.dir).catch(()=>{});throw await V.rm(A,{recursive:!0,force:!0}).catch(()=>{}),_}}async function yE(e,t,n){let r=await Z(),o=pe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ee())}async function Ys(e,t){e.step=t,await rc(e)}function bE(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 kE(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function EL(e,t,n){if(!kE(n)||Ot.has(t)||!await ge(n.box).catch(()=>null))return!1;try{await V.access(n.dir)}catch{return!1}let o=await ge(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Ot.set(t,s),vE(s,o).catch(a=>xE(s,a)),!0}async function iw(){let e=await Z(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Ot.has(r))continue;if(await EL(n,r,o)){t=!0,Te(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await re({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=bE(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Fr.has(i.state)&&(i.state="canceled"),Te(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await re({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await XT(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Fr.has(r.state)||Ot.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?Y.join(ta,`${n.id}-${r.subtaskId}`):null;await re({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await XT(n,r.name,o)}}return t=await AL(e)||t,t&&await ee(),t}async function XT(e,t,n){if(t&&await Gt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Mt(e.workspace)){let s=Y.join(n,o.name);await _t(o.base,s).then(()=>!0,()=>!1)||await _t(o.dir,s).catch(()=>{})}await V.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ge(e.leadBox).catch(()=>null);r?.cwd&&await _t(r.cwd,n).catch(()=>{})}async function AL(e){let t;try{t=await V.readdir(ta)}catch{return!1}let n=new Set;for(let o of e.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of Ot.values())o.dir&&n.add(Y.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=Y.join(ta,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Mt(a.workspace))await _t(i.base,Y.join(s,i.name)).catch(()=>{});await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ge(a.leadBox).catch(()=>null):null;i?.cwd?await _t(i.cwd,s).catch(()=>{}):await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await re({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function lw(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Ts(n))throw new Error("task (string) required");let s=await Xi(t,r),a=yo(t);if(a){let p=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=pe(await Z(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ge(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=Mt(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let h of p)if(!h?.dir||!await Es(h.dir))throw new Error(`tagged checkout "${h?.name??"?"}" is missing from the lead workspace (${h?.dir??"no dir"})`);u=p.map(h=>({name:h.name,leadDir:h.dir,base:null}))}else if(!await Es(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Po().slice(0,8),d={id:f,trackId:e,trackName:i.name,agent:t,task:Ts(n),skipPermissions:r,reason:nE(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${cw(i.name)}-${f}`,base:null,repos:u,dir:Y.join(ta,`${e}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Ot.set(f,d),await rc(d),_L(d,c,s).catch(p=>xE(d,p)),d}async function xE(e,t){e.failedStep=e.step,e.error=t instanceof Yi?"canceled":t?.message||String(t),e.step=t instanceof Yi?"canceled":"error",e.finishedAt=Date.now(),await dw(e).catch(()=>{}),await rc(e).catch(()=>{}),await OL(e).catch(()=>{})}function qu(e){let t=Ot.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 uw(e){let t=Ot.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 _L(e,t,n){if(await Ys(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await As(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await rc(e)}else e.base=await As(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Ys(e,"spawn"),await V.mkdir(ta,{recursive:!0}),e.repos){await V.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await _s(a.leadDir,Y.join(e.dir,a.name),e.branch,a.base);let s=Mt((pe(await Z(),e.trackId)||{}).workspace);await tc(e.dir,e.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),e.branch)}else await _s(t.cwd,e.dir,e.branch,e.base),await Gi(e.dir);await IL(e,t);let r=e.trackName.slice(0,38).trim(),o=await pr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await $L(e,o),await rE(e.trackId,o.name,Ij(e.task,!!e.repos),{kind:"subtask"}),await vE(e,t)}async function vE(e,t){await Ys(e,"working");let n=Y.join(e.dir,$u);if(await ML(n,e),e.canceled)throw new Yi;if(await Ys(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=Y.join(e.dir,l.name);await Cp(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:YT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Pp(l.leadDir,l.base,e.branch,zs),l.files=await Dp(l.leadDir,l.base,e.branch,zs);let f=await Bp(l.leadDir,l.base,e.branch,zs);l.applied=f.applied,l.conflicts=f.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(d=>Y.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:Y.join(l.name,d))),f.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Cp(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:YT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Pp(t.cwd,e.base,e.branch,zs),e.files=await Dp(t.cwd,e.base,e.branch,zs);let s=await Bp(t.cwd,e.base,e.branch,zs);e.applied=s.applied,e.conflicts=s.conflicts}await rc(e);let r=await CL(e,t,n);await Ys(e,"resume"),await rE(e.trackId,e.leadBox,$j(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Ys(e,"reap"),await dw(e),e.step="done";let o=Date.now();await yE(e.trackId,e.id,{...Wu(e),finishedAt:o}),await RL(e,o),e.finishedAt=o}async function IL(e,t){let n=Y.join(e.dir,Y.dirname(Hi));await V.mkdir(n,{recursive:!0});let r="";try{r=await kb(t.cwd,t.sessionId)}catch{}if(!r)try{r=await V.readFile(Y.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}
|
|
241
245
|
|
|
242
246
|
**Delegated task:** ${e.task}
|
|
243
247
|
|
|
@@ -248,15 +252,15 @@ ${a}`}async function Ch(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
248
252
|
## Conversation so far (lead \u2192 you)
|
|
249
253
|
|
|
250
254
|
${r}
|
|
251
|
-
`;await
|
|
255
|
+
`;await V.writeFile(Y.join(e.dir,Hi),o)}async function $L(e,t){let n=await Z(),r=pe(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:xa(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Te(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]||{},...Wu(e)},await ee())}async function RL(e,t=e.finishedAt){let n=await Z(),r=pe(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),Te(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 ee()}async function OL(e){let t=await Z(),n=pe(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"),Te(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]||{},...Wu(e)},await ee()}async function CL(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=Y.join(t.cwd,r);await V.mkdir(Y.dirname(o),{recursive:!0}),await V.copyFile(n,o)}catch{}return r}async function dw(e){if(e.dir&&await Zd(e.dir).catch(()=>{}),e.box&&await Gt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await _t(t.leadDir,Y.join(e.dir,t.name)).catch(()=>{});await V.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await ge(e.leadBox).catch(()=>null))?.cwd;n&&await _t(n,e.dir).catch(()=>{})}}async function ML(e,t){let n=(t.startedAt||Date.now())+vL;for(;;){if(t.canceled||t.forceMerge)return;try{await V.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await No(SL)}}function No(e){return new Promise(t=>setTimeout(t,e))}var ZT,vn,QT,Ej,Ui,Aj,Ou,Ji,Lr,Co,eE,_j,$u,Hi,Ij,$j,Ut,mh,pe,hh,Te,wh,gh,Cj,Fr,GT,Pj,Dj,Bj,jj,Uj,Wi,VT,qj,Kj,Gj,Vj,zj,Js,qi,Zj,eL,tL,oL,dE,Ki,fE,cL,Fi,yh,lL,bh,pL,Vi,mL,hL,zT,kh,Ah,ew,ea,ra,_h,Qt,Ih,nw,xh,vL,SL,ta,zs,Ot,TL,jo,Wu,rc,cw,Yi,Nr=L(()=>{Ra();lt();sp();Yk();Xa();cr();fl();e0();it();cn();Yo();dl();g0();io();_p();$p();N0();yl();Uf();pl();Zf();zt();Nl();ar();Dn();Za();Ll();ix();qp();tS();ep();Vf();rS();Am();de();hu();ku();vi();bo();Km();Ks();th();_u();ch();Ln();lu();LT();fh();KT();Zc();ZT=Tj(Sj),vn=process.env.BOSTRAT_STATE_DIR||Y.join(process.env.HOME||"/tmp",".bostrat"),QT=Y.join(vn,"tracks.json"),Ej=Y.join(vn,"events.jsonl"),Ui=Y.join(vn,"workspaces"),Aj=process.env.BOSTRAT_DEFAULT_REPO||null,Ou={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"}},Ji=Object.keys(Ou);Lr={at:0,value:null};Co=".bostrat/handoff/latest.md",eE=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;_j=/\(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\.\)$/;$u=".bostrat/handoff/subtask-result.md",Hi=".bostrat/handoff/lead-context.md",Ij=(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 ${Hi} \u2014 read that first for context. Your scoped task:
|
|
252
256
|
|
|
253
257
|
${e}
|
|
254
258
|
|
|
255
|
-
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 ${$u} (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 ${Hi} \u2014 read that first for context. Your scoped task:
|
|
256
260
|
|
|
257
261
|
${e}
|
|
258
262
|
|
|
259
|
-
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 ${$u} 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.`,$j=(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."),Ut=null,mh=null;pe=(e,t)=>e.tracks.find(n=>n.id===t)||null,hh=Promise.resolve();Te=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),Rj(e.id,r),ux(e,r),r};Tb(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await Z()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(Te(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await ee().catch(()=>{}))});wh=Y.join(vn,"plane-link","update-notice.json");gh=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),Cj=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Fr=new Set(["merged","conflict","canceled","failed","handed-off"]),GT=new Map;Pj=5e3;Dj={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};Bj=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];jj=10*6e4;Uj=1500,Wi=new Map;VT="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.";qj=4e3,Kj=3,Gj=[300,350,400],Vj=1e3,zj=12,Js=new Map,qi=new Map;Zj=[250,400,500,600];eL=3e4,tL=[350,650,900];oL=12e4;dE=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}))});Ki="# bostrat workspace",fE=`${Ki}
|
|
260
264
|
|
|
261
265
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
262
266
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -534,56 +538,91 @@ toward the chat, never the only record.
|
|
|
534
538
|
|
|
535
539
|
When you FINISH a piece of implementation work (feature built, bug fixed, checks
|
|
536
540
|
green) and the operator has not already told you to ship it: do NOT commit, push,
|
|
537
|
-
or open a PR on your own. Instead
|
|
538
|
-
|
|
539
|
-
|
|
541
|
+
or open a PR on your own. Instead OFFER to, as a structured plan \u2014 write the ship
|
|
542
|
+
you would run as JSON and run:
|
|
543
|
+
|
|
544
|
+
bostrat-ship offer <plan.json>
|
|
545
|
+
|
|
546
|
+
Plan shape: { "label": "Ship", "why": "<one line on what lands>", "ship": {
|
|
547
|
+
"actions": ["commit","push","pr"], "toPr": 641, "rebase": "offered"|"none",
|
|
548
|
+
"base": "main", "repos": ["<checkout>"] } } \u2014 actions is what you would do
|
|
549
|
+
(drop "pr" when the branch's PR is already open and name it in toPr; set
|
|
550
|
+
rebase: "offered" when the branch is behind its base); base and repos are
|
|
551
|
+
optional. Bostrat grounds the plan in git and renders one-tap ship controls in the
|
|
540
552
|
operator's chat; the go-ahead comes back as a normal message telling you exactly
|
|
541
|
-
which steps to run (and which to skip).
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
553
|
+
which steps to run (and which to skip). Run it as the LAST thing in the turn and
|
|
554
|
+
then stop \u2014 it IS your closing line. If you cannot run it, END your final message
|
|
555
|
+
with a short, explicit offer \u2014 e.g. "Want me to commit and push this, or open a
|
|
556
|
+
PR?" \u2014 as its own last line; Bostrat still detects that sentence. An offer buried
|
|
557
|
+
mid-message, phrased as a statement, or omitted entirely renders no controls \u2014 so
|
|
558
|
+
close EVERY completed implementation turn one of these two ways, every time. When
|
|
559
|
+
the operator has already asked you to commit / push / open the PR, just do it; do
|
|
560
|
+
not re-offer.
|
|
561
|
+
|
|
562
|
+
FOLLOW-UPS: when the work you just finished has a natural next step that only
|
|
563
|
+
makes sense AFTER a lifecycle moment \u2014 deploying after the merge, publishing a
|
|
564
|
+
package after the release PR lands, re-running a migration after CI goes green \u2014
|
|
565
|
+
arm it BEFORE your closing line:
|
|
566
|
+
|
|
567
|
+
bostrat-followup offer <plan.json>
|
|
568
|
+
|
|
569
|
+
Plan: { "label": "Deploy to Pi", "trigger": "pr.merged"|"ship.done"|"pr.opened"|
|
|
570
|
+
"checks.green"|"now", "prompt": "<the full instruction a fresh session could
|
|
571
|
+
execute \u2014 the Track may be revived to run it>", "why": "<one line>" }. Bostrat
|
|
572
|
+
offers it as a one-tap card at that moment; now renders the card at the end of
|
|
573
|
+
this turn (a structured "shall I do X next?" instead of a question \u2014 then stop).
|
|
574
|
+
Arm freely; it is cheap, Track-local, and the operator answers later. Don't arm
|
|
575
|
+
the generic "pick up whatever this unblocks" \u2014 that nudge already exists.
|
|
576
|
+
bostrat-followup propose <plan.json> saves a STANDING follow-up (offered on
|
|
577
|
+
every matching moment, listed in Settings) \u2014 propose one ONLY when the operator
|
|
578
|
+
asks ("always deploy after merging", "remember this") or you have seen them ask
|
|
579
|
+
for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
580
|
+
(a deleted one must never be re-proposed), keep the prompt free of this Track's
|
|
581
|
+
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
582
|
+
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
583
|
+
follow-up on its own; the operator's tap is the gate.
|
|
584
|
+
`,cL=fE,Fi=fE+ck;yh=e=>typeof e=="string"&&(e===Ki||e.startsWith(`${Ki}
|
|
585
|
+
`)),lL="# bostrat multi-repo workspace";bh=Fi.replace(`${Ki}
|
|
586
|
+
`,`${Ki}
|
|
548
587
|
|
|
549
588
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
550
589
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
551
590
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
552
591
|
When working anywhere else, ignore everything below.
|
|
553
|
-
`);xj=new Set(["claude","codex","cursor"]);Fi=new Set,vj=e=>Fi.has(e);Sj=9e4;hT={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};oh=new Map;uh=120;jh=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:wa(e.hookToken),createdAt:Date.now()});Vs=new Map,Ys=e=>Vs.get(e)||(Pt?le(Pt,e)?.handoff:null)||null;dh=6e4,Jt=(e,t,n=15e3)=>bT("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),fh=new Map,Fh=e=>fh.get(e)||null;sh=new Map;Rj=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,Oj=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,zs=J.join(yn,"worktrees"),Us=[Pi,bu],At=new Map,Cj=e=>At.get(e)||(Pt?Pt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Eo=e=>{let t=new Map([...At.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Pt?le(Pt,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)},Cu=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});Yi=e=>FT(e.trackId,e.id,Cu(e));Gh=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Hi=class extends Error{}});nt();import ke from"node:path";import at from"node:fs";import aH from"node:readline";import{fileURLToPath as iH}from"node:url";import Lt from"express";import cH from"node:http";import lH from"node:https";import hd from"node:os";import{WebSocketServer as uH}from"ws";nt();import LR from"node-pty";async function Py(e,t,n={}){if(!Ne(t)){nl(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await ir()){nl(e,`host tmux server not running on ${xt}`),e.close(1011,"tmux server unavailable");return}if(!await fe(t)){nl(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Ny(n.cols,120,Dy),o=Ny(n.rows,32,By),s;try{s=LR.spawn(W,["-S",xt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){nl(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&rf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(rf(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"&&WR(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{}HR(t)})}var FR=6,UR=250;async function HR(e,t=FR,n=UR){for(let r=0;r<t;r++){if(await Xr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Dy=20,By=6;function WR(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Dy&&Number(t)>=By}function Ny(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function rf(e,t){try{e.send(JSON.stringify(t))}catch{}}function nl(e,t){rf(e,{type:"error",message:t})}function jy(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}}qt();ol();Jf();_r();import{createRequire as dH}from"node:module";var Hj=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Yh({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(Hj)&&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 rA from"node:fs";import Pr from"node:fs/promises";import sL from"node:os";import tt from"node:path";import{spawn as aL}from"node:child_process";import{createRequire as iL}from"node:module";ue();Cl();import eA from"node:fs";import Zi from"node:fs/promises";import Kj from"node:http";import Xt from"node:path";import Pu from"node:os";import{createRequire as Gj}from"node:module";import Zh from"ws";import Wj from"node:path";import qj from"node:os";var Xi=process.env.BOSTRAT_STATE_DIR||Wj.join(process.env.HOME||qj.tmpdir(),".bostrat");function Xh(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 GT(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Xh(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function VT(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 Yt=(()=>{try{return Gj(import.meta.url)("../package.json").version||null}catch{return null}})(),zT=process.env.BOSTRAT_STATE_DIR||Xt.join(process.env.HOME||Pu.tmpdir(),".bostrat");function _o({env:e=process.env,pkgDir:t=Xt.resolve(Xt.dirname(new URL(import.meta.url).pathname),".."),exists:n=eA.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(Xt.join(t,"..",".git"))?"git-checkout":"dev"}function Qi(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 Nu(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 Vj({home:e=process.env.HOME||Pu.homedir(),env:t=process.env}={}){let n=r=>{try{return eA.statSync(r),!0}catch{return!1}};return{claude:n(Xt.join(e,".claude",".credentials.json")),codex:n(Xt.join(t.CODEX_HOME||Xt.join(e,".codex"),"auth.json"))}}var et=1,JT=1e3,YT=3e4,zj=6e4,Jj=1500,Yj=500,Nr=100,Xj=2e3,Zj=25e3,Qj=6e4,tA=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},eL=tA(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),tL=3e4,XT=10*6e4,nL=400,ZT=250,rL=tA(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Qh=4,oL=6e4,QT=6e4;function nA({url:e,token:t,nodeId:n=Pu.hostname(),name:r=Pu.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=oL,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:d=null,updatePolicy:f=null,installKind:p=_o(),selfUrl:m=`http://127.0.0.1:${process.env.PORT||3362}`,onDegraded:w=null,onRecovered:y=null,ingestMode:x=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:T=eL,unackedRetryMs:I=Qj,pingMs:$=Zj,maxInflightEvents:D=nL,httpTimeoutMs:S=rL,stallBackoffMinMs:A=tL,fetchImpl:R=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=VT(e);let L=m.replace(/^http/,"ws"),U=new Set,se=new Map,G=new Map,C=Xt.join(zT,"plane-link","cursor.json"),Z=Xt.join(zT,"plane-link","dead-letter.ndjson"),Y=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),te={},we={},ce=new Map,V=null,ge=null,B=0,ye=0,be=0,_e=Promise.resolve(),Ie="ws",b=0,h=!1,g=0,_=!1,ne=Nr,ae=new Set,Te=[],Ee=null,Le=null,bt=!1,bd=!1,da=JT,fa=null,Do=null,Bo=null,vn=null,Sn=null,kd=!1,Ac=0;async function wI(){try{te=JSON.parse(await Zi.readFile(C,"utf8"))}catch{te={}}}function gI(){kd=!0,!Bo&&(Bo=setTimeout(async()=>{if(Bo=null,!!kd){kd=!1;try{await Zi.mkdir(Xt.dirname(C),{recursive:!0}),await Zi.writeFile(C,JSON.stringify(te))}catch(N){E.error("[plane-link] cursor flush failed:",N?.message||N)}}},Xj),Bo.unref?.())}function Ze(N){return!Le||Le.readyState!==Zh.OPEN?!1:(Le.send(JSON.stringify(N)),!0)}function pa(N){let P=Date.now();for(let{trackId:k,event:M}of N){let H=ce.get(M.id);ce.set(M.id,{trackId:k,seq:M.seq,event:M,sentAt:P,firstSentAt:H?.firstSentAt??P,retryAt:P+I,tries:(H?.tries??0)+1,nacks:H?.nacks??0})}return Ie==="http"?(xI(N),!0):Ze({v:et,ch:"events",type:"append",events:N})}function yI(N){let P=null;for(let k of ce.values())k.trackId===N&&(P==null||k.seq<P)&&(P=k.seq);return P}function xd(N){let P=we[N];if(P==null)return;let k=yI(N),M=k==null?P:Math.min(P,k-1);M>(te[N]??0)&&(te[N]=M,gI())}function ma(){if(V=Date.now(),ge!=null){let N=Date.now()-ge;E.log(`[plane-link] spine sync recovered after ${Math.round(N/1e3)}s degraded (${B} stall redial(s))`);let P=ge;ge=null,B=0,ye=0,h=!1,g=0;try{y?.({since:P,downMs:N})}catch(k){E.error(`[plane-link] onRecovered failed: ${k?.message||k}`)}}}function vd(N){if(!Array.isArray(N)||!N.length)return;let P=new Set;for(let k of N){let M=ce.get(k);M&&(ce.delete(k),(we[M.trackId]??0)<M.seq&&(we[M.trackId]=M.seq),P.add(M.trackId))}for(let k of P)xd(k);ma()}function lg(N,P){be++;let k={at:Date.now(),trackId:N.trackId,seq:N.seq,id:N.event?.id??null,type:N.event?.type??null,reason:P,event:N.event??null};E.error(`[plane-link] DEAD-LETTER ${N.trackId} seq ${N.seq} (${k.type??"?"}): plane rejected it permanently (${P}) \u2014 kept in ${Z}`),_e=_e.then(async()=>{await Zi.mkdir(Xt.dirname(Z),{recursive:!0}),await Zi.appendFile(Z,JSON.stringify(k)+`
|
|
554
|
-
`)}).catch(M=>E.error(`[plane-link] dead-letter write failed: ${M?.message||M}`))}function ug(N,P,k,M=null){if(!Array.isArray(N)||!N.length)return;let H=new Set,pe=Date.now();for(let Se of N){let Pe=ce.get(Se);Pe&&(k?(ce.delete(Se),lg(Pe,P),(we[Pe.trackId]??0)<Pe.seq&&(we[Pe.trackId]=Pe.seq),H.add(Pe.trackId)):(Pe.nacks++,Pe.retryAt=pe+Math.max(1e3,Number(M)||I)))}for(let Se of H)xd(Se);ma()}function dg(N=Date.now()){let P=1/0;for(let k of ce.values())k.firstSentAt<P&&(P=k.firstSentAt);return P===1/0?0:N-P}function fg(){if(!bt||!ce.size)return;let N=Date.now(),P=dg(N);if(P<T||V!=null&&N-V<T||Ie==="http"&&h||ye>N)return;B++;let k=Math.min(A*2**(B-1),XT);if(ge==null){ge=N-P,E.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(P/1e3)}s with ${ce.size} event(s) in flight (${Ie} door) \u2014 the plane is not answering appends`);try{w?.({since:ge,unacked:ce.size,oldestAgeMs:P,mode:Ie})}catch(M){E.error(`[plane-link] onDegraded failed: ${M?.message||M}`)}}if(Ie==="http"){h=!0,g=N+k,E.error(`[plane-link] ingest circuit OPEN (stall #${B}); probing /v1/ingest/health in ${Math.round(k/1e3)}s`);return}ye=N+k,E.error(`[plane-link] closing the link and redialing in ${Math.round(k/1e3)}s (stall redial #${B})`);try{Le?.terminate()}catch{}}function Sd(){if(!bt)return;let N=Date.now();if(Ie==="http"&&h)return;let P=[...ce.values()].filter(M=>M.event&&M.retryAt<=N&&!(Ie==="http"&&ae.has(M.trackId)));if(!P.length)return;P.sort((M,H)=>M.trackId===H.trackId?M.seq-H.seq:M.trackId<H.trackId?-1:1),E.log(`[plane-link] re-sending ${P.length} unacked event(s) the plane never confirmed`);let k=Ie==="http"?ne:Nr;if(Ie==="http"){let M=new Map;for(let H of P)(M.get(H.trackId)??M.set(H.trackId,[]).get(H.trackId)).push(H);for(let[,H]of M){if(b>=Qh)return;pa(H.slice(0,k).map(pe=>({trackId:pe.trackId,event:pe.event})))}return}for(let M=0;M<P.length;M+=k){let H=P.slice(M,M+k).map(pe=>({trackId:pe.trackId,event:pe.event}));if(!pa(H))return}}async function bI(N=null){for(;;){if(!bt)return!1;let P=ce.size<D,k=Ie!=="http"||b<Qh&&!h&&(!N||!ae.has(N));if(P&&k)return!0;await new Promise(M=>setTimeout(M,100))}}function Td(){if(Ee=null,!bt||!Te.length)return;let N=Te;if(Te=[],Ie!=="http"){for(let k=0;k<N.length;k+=Nr)pa(N.slice(k,k+Nr));return}let P=new Map;for(let k of N)(P.get(k.trackId)??P.set(k.trackId,[]).get(k.trackId)).push(k);for(let[k,M]of P){if(ae.has(k)||b>=Qh||h){Te.push(...M);continue}for(let H=0;H<M.length;H+=ne)pa(M.slice(H,H+ne))}Te.length&&!Ee&&(Ee=setTimeout(Td,ZT),Ee.unref?.())}let kI=N=>{let P=new Map;for(let{trackId:k,event:M}of N)(P.get(k)??P.set(k,[]).get(k)).push(M);return[...P].map(([k,M])=>({trackId:k,events:M}))},jo=(N,P,k,{progress:M})=>{let H=Date.now();for(let{event:pe}of N){let Se=ce.get(pe.id);Se&&(Se.nacks++,Se.retryAt=H+Math.max(1e3,k||I))}M&&ma()};async function xI(N){let P=new Set(N.map(k=>k.trackId));for(let k of P)ae.add(k);b++;try{let k=await R(`${Y}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:kI(N)}),signal:AbortSignal.timeout(S)}),M=(Number(k.headers.get("retry-after"))||0)*1e3;if(k.status===200){let H=await k.json();ne=Nr;for(let pe of H.results??[]){vd(pe.acked??[]);let Se=(Pe,pt)=>{let $t=new Map;for(let Ge of Pe??[])($t.get(Ge.reason)??$t.set(Ge.reason,[]).get(Ge.reason)).push(Ge.id);for(let[Ge,gg]of $t)ug(gg,Ge,pt,H.retryAfterMs??null)};Se(pe.rejected,!0),Se(pe.deferred,!1)}vd(H.discarded??[]),ma();return}if(k.status===429){jo(N,"overloaded",M||5e3,{progress:!0});return}if(k.status===413&&N.length>1){ne=Math.max(1,Math.floor(N.length/2)),E.error(`[plane-link] ingest request too large (${N.length} events) \u2014 halving batches to ${ne}`),jo(N,"too_large",1e3,{progress:!0});return}if(k.status===413){let H=ce.get(N[0].event.id);H&&(ce.delete(H.event.id),lg(H,"too_large"),(we[H.trackId]??0)<H.seq&&(we[H.trackId]=H.seq),xd(H.trackId)),ma();return}if(k.status===401||k.status===403){if(Date.now()-Ac>=QT){Ac=Date.now();let H="";try{H=(await k.json())?.error??""}catch{}E.error(`[plane-link] plane refused ingest (HTTP ${k.status}${H?`: ${H}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}jo(N,`http_${k.status}`,6e4,{progress:!1});return}if(k.status===400){E.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${N.length} event(s) for retry; this is a bug, not data`),jo(N,"http_400",6e4,{progress:!1});return}jo(N,`http_${k.status}`,M||5e3,{progress:!1})}catch(k){let M=k?.name==="TimeoutError"||k?.name==="AbortError";M&&N.length>1&&(ne=Math.max(1,Math.floor(N.length/2)),E.error(`[plane-link] ingest request of ${N.length} event(s) outlived its ${S} ms deadline \u2014 halving batches to ${ne}`)),jo(N,M?"timeout":"network",5e3,{progress:!1})}finally{b--;for(let k of P)ae.delete(k)}}async function vI(){if(Ie!=="http"||!h||!bt||_)return;let N=Date.now();if(!(g>N)){_=!0;try{let P=await R(`${Y}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(S)});if(!P.ok)throw new Error(`HTTP ${P.status}`);E.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),h=!1,g=0,ye=Date.now()+S+I+1e3;for(let k of ce.values())k.retryAt=Math.min(k.retryAt,Date.now());Sd()}catch(P){B++;let k=Math.min(A*2**(B-1),XT);g=Date.now()+k,E.error(`[plane-link] ingest door still not answering (${P?.message||P}); next probe in ${Math.round(k/1e3)}s`)}finally{_=!1}}}function pg(){let N=Date.now();return{mode:Ie,unacked:ce.size,oldestUnackedAgeMs:dg(N),lastProgressAt:V,degradedSince:ge,stallRedials:B,deadLettered:be,http:Ie==="http"?{inFlight:b,circuitOpen:h,nextProbeInMs:h?Math.max(0,g-N):0,batchCap:ne}:null}}async function mg(){if(!i)return;let N;try{N=await i()}catch(P){E.error("[plane-link] metrics sample failed:",P?.message||P);return}N&&Ze({v:et,ch:"ctl",type:"metrics",metrics:{...N,planeLink:pg()}})}function SI(){i&&(Sn&&clearInterval(Sn),mg(),Sn=setInterval(mg,c),Sn.unref?.())}async function TI(){let N;try{N=await o()}catch(P){E.error("[plane-link] backfill listTracks failed:",P?.message||P);return}Ze({v:et,ch:"ctl",type:"tracks",ids:N.filter(P=>!P.transferredTo).map(P=>P.id).filter(P=>typeof P=="string")});for(let P of N){if(!bt)return;let k;try{k=await F(P)}catch{continue}let M=te[P.id]??0,H=k.filter(Se=>Se.seq>M&&typeof Se.id=="string"),pe=Ie==="http"?ne:Nr;for(let Se=0;Se<H.length;Se+=pe){if(!await bI(P.id))return;let Pe=H.slice(Se,Se+Math.min(pe,Ie==="http"?ne:Nr)).map(pt=>({trackId:P.id,event:pt}));if(!pa(Pe))return}}}function hg(N){!bt||!N?.trackId||typeof N.id!="string"||(Te.push({trackId:N.trackId,event:N}),Ee||(Ee=setTimeout(Td,ZT),Ee.unref?.()))}let ha=new Map;async function AI(N){let{reqId:P,method:k,path:M,headers:H={},body:pe}=N,Se=Object.fromEntries(Object.entries(H).filter(([,pt])=>pt!=null)),Pe=new AbortController;ha.set(P,Pe);try{if(N.host){await EI(N,Se,Pe);return}let pt=await fetch(m+M,{method:k,headers:Se,body:pe?Buffer.from(pe,"base64"):void 0,signal:Pe.signal}),$t=Object.fromEntries(pt.headers);for(let Ge of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete $t[Ge];if($t["content-type"]||($t["content-type"]="application/octet-stream"),Ze({v:et,ch:"control",type:"http_head",reqId:P,status:pt.status,headers:$t}),pt.body){for await(let Ge of pt.body)if(!Ze({v:et,ch:"control",type:"http_data",reqId:P,data:Buffer.from(Ge).toString("base64")}))break}Ze({v:et,ch:"control",type:"http_end",reqId:P,error:null})}finally{ha.delete(P)}}function EI(N,P,k){let{reqId:M,method:H,path:pe,body:Se}=N,Pe=new URL(m);return new Promise(pt=>{let $t=Kj.request({host:Pe.hostname,port:Pe.port,method:H,path:pe,headers:{...P,host:N.host},signal:k.signal},Ge=>{let{connection:gg,"keep-alive":$H,"transfer-encoding":RH,upgrade:OH,..._I}=Ge.headers;Ze({v:et,ch:"control",type:"http_head",reqId:M,status:Ge.statusCode||502,headers:_I}),Ge.on("data",II=>{Ze({v:et,ch:"control",type:"http_data",reqId:M,data:Buffer.from(II).toString("base64")})||Ge.destroy()}),Ge.on("end",()=>{Ze({v:et,ch:"control",type:"http_end",reqId:M,error:null}),pt()}),Ge.on("error",()=>{Ze({v:et,ch:"control",type:"http_end",reqId:M,error:"view upstream error"}),pt()})});$t.on("error",Ge=>{Ze({v:et,ch:"control",type:"http_end",reqId:M,error:Ge?.message||"view request failed"}),pt()}),Se&&$t.write(Buffer.from(Se,"base64")),$t.end()})}function wg(){if(bd)return;Le=new Zh(e,{headers:{authorization:`Bearer ${t}`}}),Le.on("open",async()=>{let P=null;try{P=await s?.()||null}catch{P=null}Ze({v:et,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Yt,installKind:p,...typeof f=="function"?{updatePolicy:f()}:f?{updatePolicy:f}:{},providers:Vj(),...P?{agents:P}:{}})}),Le.on("message",P=>{let k;try{k=JSON.parse(P)}catch{return}if(k.ch==="ctl"&&k.type==="hello_ok"){bt=!0,da=JT,fa=null,E.log(`[plane-link] connected to plane as ${n}`),k.minNodeVersion&&Yt&&Qi(Yt,k.minNodeVersion)&&E.error(`[plane-link] this node is bostrat-node ${Yt}; the plane asks for \u2265${k.minNodeVersion} \u2014 update with: ${Nu(p)}`);try{u?.(typeof k.updatePolicy=="string"?k.updatePolicy:null)}catch{}let M=typeof k.latestNodeVersion=="string"?k.latestNodeVersion:null;if(M&&Yt&&Qi(Yt,M)&&!U.has(M)){U.add(M),E.log(`[plane-link] bostrat-node ${M} is available (this node runs ${Yt}) \u2014 ${Nu(p)}`);try{l?.({current:Yt,latest:M,min:k.minNodeVersion??null,installKind:p,required:!!(k.minNodeVersion&&Qi(Yt,k.minNodeVersion))})}catch(pe){E.error(`[plane-link] update notice failed: ${pe?.message||pe}`)}}let H=k.ingest==="http";Ie=x==="ws"?"ws":x==="http"||H?"http":"ws",x==="http"&&!H&&E.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),Ie==="http"&&E.log("[plane-link] spine rides the HTTPS ingest door"),h=!1,g=0,ne=Nr,vn&&clearInterval(vn),vn=setInterval(()=>{Ze({v:et,ch:"ctl",type:"ping"}),fg(),vI().catch(()=>{}),Sd(),Te.length&&!Ee&&Td()},$),vn.unref?.(),SI(),TI().catch(pe=>E.error("[plane-link] backfill failed:",pe?.message||pe));return}if(k.ch==="ctl"&&k.type==="hello_refused"){let M=typeof k.minNodeVersion=="string"?k.minNodeVersion:null,H=typeof k.latestNodeVersion=="string"?k.latestNodeVersion:null;E.error(`[plane-link] plane REFUSED this node: bostrat-node ${Yt??"?"} is below its minimum ${M??"?"} \u2014 ${k.fix||Nu(p)}`),da=YT,fa=0;try{d?.({current:Yt,min:M,latest:H,fix:k.fix||Nu(p),installKind:p})}catch(pe){E.error(`[plane-link] refusal handler failed: ${pe?.message||pe}`)}return}if(k.ch==="ctl"&&k.type==="update_policy"){try{u?.(typeof k.mode=="string"?k.mode:null)}catch{}return}if(k.ch==="ctl"&&k.type==="usage_caps"){Ol(k.caps??{},{source:"plane",updatedAt:k.updatedAt??Date.now()}).then(M=>E.log(`[plane-link] spend caps ${Object.keys(M.caps).length?`updated (${Object.keys(M.caps).join(", ")})`:"cleared"} from plane`)).catch(M=>E.error("[plane-link] spend-caps push rejected:",M?.message||M));return}if(k.ch==="ctl"&&k.type==="transferred"&&Array.isArray(k.tracks)){Promise.resolve().then(()=>a?.(k.tracks)).catch(M=>E.error("[plane-link] transfer notice failed:",M?.message||M));return}if(k.ch==="events"&&k.type==="ack"&&Array.isArray(k.ids)){vd(k.ids);return}if(k.ch==="events"&&k.type==="nack"&&Array.isArray(k.ids)){ug(k.ids,k.reason,k.permanent===!0,k.retryAfterMs??null);return}if(k.ch==="control"&&k.type==="http_open"){AI(k).catch(M=>Ze({v:et,ch:"control",type:"http_end",reqId:k.reqId,error:M?.message||"tunnel error"}));return}if(k.ch==="control"&&k.type==="http_cancel"){ha.get(k.reqId)?.abort();return}if(k.ch==="attach"&&k.type==="open"){let M=new Zh(L+k.path,Array.isArray(k.protocols)?k.protocols:[],k.host?{headers:{host:k.host}}:void 0);se.set(k.streamId,M),G.set(k.streamId,[]),M.on("open",()=>{let pe=G.get(k.streamId)||[];G.delete(k.streamId);for(let{buf:Se,bin:Pe}of pe)M.send(Se,{binary:Pe})}),M.on("message",(pe,Se)=>{let Pe=Se?pe:Buffer.from(String(pe));Ze({v:et,ch:"attach",type:"data",streamId:k.streamId,data:Pe.toString("base64"),...Se?{bin:!0}:{}})});let H=()=>{G.delete(k.streamId),se.delete(k.streamId)&&Ze({v:et,ch:"attach",type:"close",streamId:k.streamId})};M.on("close",H),M.on("error",H);return}if(k.ch==="attach"&&k.type==="data"){let M=se.get(k.streamId);if(!M)return;let H=Buffer.from(k.data,"base64"),pe=k.bin===!0,Se=G.get(k.streamId);Se?Se.push({buf:H,bin:pe}):M.send(H,{binary:pe});return}if(k.ch==="attach"&&k.type==="close"){let M=se.get(k.streamId);se.delete(k.streamId),G.delete(k.streamId),M?.close();return}});let N=()=>{bt&&E.log("[plane-link] disconnected from plane"),bt=!1,vn&&(clearInterval(vn),vn=null),Sn&&(clearInterval(Sn),Sn=null),ce.clear(),we={},Te=[],Ee&&(clearTimeout(Ee),Ee=null),ae.clear();for(let M of se.values())M.close();se.clear(),G.clear();for(let M of ha.values())M.abort();if(ha.clear(),bd||Do)return;fa==null&&(fa=Date.now());let P=Date.now()-fa<zj,k=Math.max(P?Jj+Math.random()*Yj:da,ye-Date.now());Do=setTimeout(()=>{Do=null,wg()},k),Do.unref?.(),P||(da=Math.min(da*2,YT))};Le.on("close",N),Le.on("error",P=>{let k=/Unexpected server response: (\d+)/.exec(P?.message||"")?.[1];(k==="401"||k==="403")&&Date.now()-Ac>=QT&&(Ac=Date.now(),E.error(`[plane-link] plane refused this node's credentials (HTTP ${k}) \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)`)),N()})}return ht.on("event",hg),wI().then(wg),{stop:()=>{bd=!0,ht.off("event",hg),Do&&clearTimeout(Do),Bo&&clearTimeout(Bo),vn&&clearInterval(vn),Sn&&clearInterval(Sn),Ee&&clearTimeout(Ee),Ze({v:et,ch:"ctl",type:"bye",reason:"shutdown"}),Le?.close()},status:()=>({connected:bt,nodeId:n,cursor:{...te},inflight:ce.size,...pg()}),_tick:()=>{fg(),Sd()}}}sl();var Du=(()=>{try{return iL(import.meta.url)("../package.json").version||null}catch{return null}})(),cL=process.env.BOSTRAT_STATE_DIR||tt.join(process.env.HOME||sL.tmpdir(),".bostrat"),oA=tt.join(cL,"self-update"),Bu=tt.join(oA,"status.json"),lL=1440*6e4,uL=6e4,ec=new Set(["npm-global","git-checkout"]);function sA({kind:e=_o(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:ec.has(e)?e==="git-checkout"||dL(t)?"auto":"prompt":"off"}function dL(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var _t=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function fL({kind:e,to:t,from:n=Du,statusFile:r=Bu,logFile:o=tt.join(oA,"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||tt.resolve(tt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!ec.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(y,x="")=>`write_status ${_t(y)} ${x}`,f=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${_t(i)}`,p=`
|
|
592
|
+
`);pL=new Set(["claude","codex","cursor"]);Vi=new Set,mL=e=>Vi.has(e);hL=9e4;zT={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};kh=new Map;Ah=120;ew=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:xa(e.hookToken),createdAt:Date.now()});ea=new Map,ra=e=>ea.get(e)||(Ut?pe(Ut,e)?.handoff:null)||null;_h=6e4,Qt=(e,t,n=15e3)=>ZT("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Ih=new Map,nw=e=>Ih.get(e)||null;xh=new Map;vL=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,SL=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ta=Y.join(vn,"worktrees"),zs=[Hi,$u],Ot=new Map,TL=e=>Ot.get(e)||(Ut?Ut.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,jo=e=>{let t=new Map([...Ot.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Ut?pe(Ut,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)},Wu=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});rc=e=>yE(e.trackId,e.id,Wu(e));cw=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Yi=class extends Error{}});lt();import xe from"node:path";import mt from"node:fs";import ZH from"node:readline";import{fileURLToPath as QH}from"node:url";import Wt from"express";import eW from"node:http";import tW from"node:https";import Ad from"node:os";import{WebSocketServer as nW}from"ws";lt();import gO from"node-pty";async function Zy(e,t,n={}){if(!Ne(t)){ll(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await fr()){ll(e,`host tmux server not running on ${At}`),e.close(1011,"tmux server unavailable");return}if(!await ge(t)){ll(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Xy(n.cols,120,Qy),o=Xy(n.rows,32,eb),s;try{s=gO.spawn(K,["-S",At,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ll(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&mf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(mf(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&xO(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{}kO(t)})}var yO=6,bO=250;async function kO(e,t=yO,n=bO){for(let r=0;r<t;r++){if(await oo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Qy=20,eb=6;function xO(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Qy&&Number(t)>=eb}function Xy(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function mf(e,t){try{e.send(JSON.stringify(t))}catch{}}function ll(e,t){mf(e,{type:"error",message:t})}function tb(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}}zt();dl();sp();Nr();import{createRequire as rW}from"node:module";var NL=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function fw({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(NL)&&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 PE from"node:fs";import Hr from"node:fs/promises";import XL from"node:os";import at from"node:path";import{spawn as ZL}from"node:child_process";import{createRequire as QL}from"node:module";de();Ll();import CE from"node:fs";import sc from"node:fs/promises";import BL from"node:http";import tn from"node:path";import Gu from"node:os";import{createRequire as jL}from"node:module";import mw from"ws";import PL from"node:path";import DL from"node:os";var oc=process.env.BOSTRAT_STATE_DIR||PL.join(process.env.HOME||DL.tmpdir(),".bostrat");function pw(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 TE(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=pw(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function EE(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 en=(()=>{try{return jL(import.meta.url)("../package.json").version||null}catch{return null}})(),AE=process.env.BOSTRAT_STATE_DIR||tn.join(process.env.HOME||Gu.tmpdir(),".bostrat");function Lo({env:e=process.env,pkgDir:t=tn.resolve(tn.dirname(new URL(import.meta.url).pathname),".."),exists:n=CE.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(tn.join(t,"..",".git"))?"git-checkout":"dev"}function ac(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 Ku(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 LL({home:e=process.env.HOME||Gu.homedir(),env:t=process.env}={}){let n=r=>{try{return CE.statSync(r),!0}catch{return!1}};return{claude:n(tn.join(e,".claude",".credentials.json")),codex:n(tn.join(t.CODEX_HOME||tn.join(e,".codex"),"auth.json"))}}var st=1,_E=1e3,IE=3e4,FL=6e4,UL=1500,HL=500,Ur=100,WL=2e3,qL=25e3,KL=6e4,ME=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},GL=ME(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),VL=3e4,$E=10*6e4,zL=400,RE=250,YL=ME(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),hw=4,JL=6e4,OE=6e4;function NE({url:e,token:t,nodeId:n=Gu.hostname(),name:r=Gu.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=JL,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:f=null,updatePolicy:d=null,installKind:p=Lo(),selfUrl:h=`http://127.0.0.1:${process.env.PORT||3362}`,onDegraded:w=null,onRecovered:g=null,ingestMode:k=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:S=GL,unackedRetryMs:A=KL,pingMs:R=qL,maxInflightEvents:B=zL,httpTimeoutMs:T=YL,stallBackoffMinMs:_=VL,fetchImpl:O=globalThis.fetch,log:I=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=EE(e);let U=h.replace(/^http/,"ws"),H=new Set,ae=new Map,J=new Map,C=tn.join(AE,"plane-link","cursor.json"),Q=tn.join(AE,"plane-link","dead-letter.ndjson"),ve=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),W={},be={},se=new Map,ue=null,me=null,j=0,ie=0,le=0,Ae=Promise.resolve(),$e="ws",He=0,ze=!1,E=0,ne=!1,Ke=Ur,y=new Set,m=[],b=null,M=null,X=!1,he=!1,De=_E,Ye=null,tt=null,rr=null,An=null,_n=null,Rd=!1,Cc=0;async function zI(){try{W=JSON.parse(await sc.readFile(C,"utf8"))}catch{W={}}}function YI(){Rd=!0,!rr&&(rr=setTimeout(async()=>{if(rr=null,!!Rd){Rd=!1;try{await sc.mkdir(tn.dirname(C),{recursive:!0}),await sc.writeFile(C,JSON.stringify(W))}catch(P){I.error("[plane-link] cursor flush failed:",P?.message||P)}}},WL),rr.unref?.())}function ot(P){return!M||M.readyState!==mw.OPEN?!1:(M.send(JSON.stringify(P)),!0)}function ya(P){let D=Date.now();for(let{trackId:x,event:N}of P){let q=se.get(N.id);se.set(N.id,{trackId:x,seq:N.seq,event:N,sentAt:D,firstSentAt:q?.firstSentAt??D,retryAt:D+A,tries:(q?.tries??0)+1,nacks:q?.nacks??0})}return $e==="http"?(QI(P),!0):ot({v:st,ch:"events",type:"append",events:P})}function JI(P){let D=null;for(let x of se.values())x.trackId===P&&(D==null||x.seq<D)&&(D=x.seq);return D}function Od(P){let D=be[P];if(D==null)return;let x=JI(P),N=x==null?D:Math.min(D,x-1);N>(W[P]??0)&&(W[P]=N,YI())}function ba(){if(ue=Date.now(),me!=null){let P=Date.now()-me;I.log(`[plane-link] spine sync recovered after ${Math.round(P/1e3)}s degraded (${j} stall redial(s))`);let D=me;me=null,j=0,ie=0,ze=!1,E=0;try{g?.({since:D,downMs:P})}catch(x){I.error(`[plane-link] onRecovered failed: ${x?.message||x}`)}}}function Cd(P){if(!Array.isArray(P)||!P.length)return;let D=new Set;for(let x of P){let N=se.get(x);N&&(se.delete(x),(be[N.trackId]??0)<N.seq&&(be[N.trackId]=N.seq),D.add(N.trackId))}for(let x of D)Od(x);ba()}function Ag(P,D){le++;let x={at:Date.now(),trackId:P.trackId,seq:P.seq,id:P.event?.id??null,type:P.event?.type??null,reason:D,event:P.event??null};I.error(`[plane-link] DEAD-LETTER ${P.trackId} seq ${P.seq} (${x.type??"?"}): plane rejected it permanently (${D}) \u2014 kept in ${Q}`),Ae=Ae.then(async()=>{await sc.mkdir(tn.dirname(Q),{recursive:!0}),await sc.appendFile(Q,JSON.stringify(x)+`
|
|
593
|
+
`)}).catch(N=>I.error(`[plane-link] dead-letter write failed: ${N?.message||N}`))}function _g(P,D,x,N=null){if(!Array.isArray(P)||!P.length)return;let q=new Set,ye=Date.now();for(let Ee of P){let Pe=se.get(Ee);Pe&&(x?(se.delete(Ee),Ag(Pe,D),(be[Pe.trackId]??0)<Pe.seq&&(be[Pe.trackId]=Pe.seq),q.add(Pe.trackId)):(Pe.nacks++,Pe.retryAt=ye+Math.max(1e3,Number(N)||A)))}for(let Ee of q)Od(Ee);ba()}function Ig(P=Date.now()){let D=1/0;for(let x of se.values())x.firstSentAt<D&&(D=x.firstSentAt);return D===1/0?0:P-D}function $g(){if(!X||!se.size)return;let P=Date.now(),D=Ig(P);if(D<S||ue!=null&&P-ue<S||$e==="http"&&ze||ie>P)return;j++;let x=Math.min(_*2**(j-1),$E);if(me==null){me=P-D,I.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(D/1e3)}s with ${se.size} event(s) in flight (${$e} door) \u2014 the plane is not answering appends`);try{w?.({since:me,unacked:se.size,oldestAgeMs:D,mode:$e})}catch(N){I.error(`[plane-link] onDegraded failed: ${N?.message||N}`)}}if($e==="http"){ze=!0,E=P+x,I.error(`[plane-link] ingest circuit OPEN (stall #${j}); probing /v1/ingest/health in ${Math.round(x/1e3)}s`);return}ie=P+x,I.error(`[plane-link] closing the link and redialing in ${Math.round(x/1e3)}s (stall redial #${j})`);try{M?.terminate()}catch{}}function Md(){if(!X)return;let P=Date.now();if($e==="http"&&ze)return;let D=[...se.values()].filter(N=>N.event&&N.retryAt<=P&&!($e==="http"&&y.has(N.trackId)));if(!D.length)return;D.sort((N,q)=>N.trackId===q.trackId?N.seq-q.seq:N.trackId<q.trackId?-1:1),I.log(`[plane-link] re-sending ${D.length} unacked event(s) the plane never confirmed`);let x=$e==="http"?Ke:Ur;if($e==="http"){let N=new Map;for(let q of D)(N.get(q.trackId)??N.set(q.trackId,[]).get(q.trackId)).push(q);for(let[,q]of N){if(He>=hw)return;ya(q.slice(0,x).map(ye=>({trackId:ye.trackId,event:ye.event})))}return}for(let N=0;N<D.length;N+=x){let q=D.slice(N,N+x).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!ya(q))return}}async function XI(P=null){for(;;){if(!X)return!1;let D=se.size<B,x=$e!=="http"||He<hw&&!ze&&(!P||!y.has(P));if(D&&x)return!0;await new Promise(N=>setTimeout(N,100))}}function Nd(){if(b=null,!X||!m.length)return;let P=m;if(m=[],$e!=="http"){for(let x=0;x<P.length;x+=Ur)ya(P.slice(x,x+Ur));return}let D=new Map;for(let x of P)(D.get(x.trackId)??D.set(x.trackId,[]).get(x.trackId)).push(x);for(let[x,N]of D){if(y.has(x)||He>=hw||ze){m.push(...N);continue}for(let q=0;q<N.length;q+=Ke)ya(N.slice(q,q+Ke))}m.length&&!b&&(b=setTimeout(Nd,RE),b.unref?.())}let ZI=P=>{let D=new Map;for(let{trackId:x,event:N}of P)(D.get(x)??D.set(x,[]).get(x)).push(N);return[...D].map(([x,N])=>({trackId:x,events:N}))},zo=(P,D,x,{progress:N})=>{let q=Date.now();for(let{event:ye}of P){let Ee=se.get(ye.id);Ee&&(Ee.nacks++,Ee.retryAt=q+Math.max(1e3,x||A))}N&&ba()};async function QI(P){let D=new Set(P.map(x=>x.trackId));for(let x of D)y.add(x);He++;try{let x=await O(`${ve}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:ZI(P)}),signal:AbortSignal.timeout(T)}),N=(Number(x.headers.get("retry-after"))||0)*1e3;if(x.status===200){let q=await x.json();Ke=Ur;for(let ye of q.results??[]){Cd(ye.acked??[]);let Ee=(Pe,kt)=>{let Dt=new Map;for(let Je of Pe??[])(Dt.get(Je.reason)??Dt.set(Je.reason,[]).get(Je.reason)).push(Je.id);for(let[Je,Ng]of Dt)_g(Ng,Je,kt,q.retryAfterMs??null)};Ee(ye.rejected,!0),Ee(ye.deferred,!1)}Cd(q.discarded??[]),ba();return}if(x.status===429){zo(P,"overloaded",N||5e3,{progress:!0});return}if(x.status===413&&P.length>1){Ke=Math.max(1,Math.floor(P.length/2)),I.error(`[plane-link] ingest request too large (${P.length} events) \u2014 halving batches to ${Ke}`),zo(P,"too_large",1e3,{progress:!0});return}if(x.status===413){let q=se.get(P[0].event.id);q&&(se.delete(q.event.id),Ag(q,"too_large"),(be[q.trackId]??0)<q.seq&&(be[q.trackId]=q.seq),Od(q.trackId)),ba();return}if(x.status===401||x.status===403){if(Date.now()-Cc>=OE){Cc=Date.now();let q="";try{q=(await x.json())?.error??""}catch{}I.error(`[plane-link] plane refused ingest (HTTP ${x.status}${q?`: ${q}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}zo(P,`http_${x.status}`,6e4,{progress:!1});return}if(x.status===400){I.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${P.length} event(s) for retry; this is a bug, not data`),zo(P,"http_400",6e4,{progress:!1});return}zo(P,`http_${x.status}`,N||5e3,{progress:!1})}catch(x){let N=x?.name==="TimeoutError"||x?.name==="AbortError";N&&P.length>1&&(Ke=Math.max(1,Math.floor(P.length/2)),I.error(`[plane-link] ingest request of ${P.length} event(s) outlived its ${T} ms deadline \u2014 halving batches to ${Ke}`)),zo(P,N?"timeout":"network",5e3,{progress:!1})}finally{He--;for(let x of D)y.delete(x)}}async function e$(){if($e!=="http"||!ze||!X||ne)return;let P=Date.now();if(!(E>P)){ne=!0;try{let D=await O(`${ve}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(T)});if(!D.ok)throw new Error(`HTTP ${D.status}`);I.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),ze=!1,E=0,ie=Date.now()+T+A+1e3;for(let x of se.values())x.retryAt=Math.min(x.retryAt,Date.now());Md()}catch(D){j++;let x=Math.min(_*2**(j-1),$E);E=Date.now()+x,I.error(`[plane-link] ingest door still not answering (${D?.message||D}); next probe in ${Math.round(x/1e3)}s`)}finally{ne=!1}}}function Rg(){let P=Date.now();return{mode:$e,unacked:se.size,oldestUnackedAgeMs:Ig(P),lastProgressAt:ue,degradedSince:me,stallRedials:j,deadLettered:le,http:$e==="http"?{inFlight:He,circuitOpen:ze,nextProbeInMs:ze?Math.max(0,E-P):0,batchCap:Ke}:null}}async function Og(){if(!i)return;let P;try{P=await i()}catch(D){I.error("[plane-link] metrics sample failed:",D?.message||D);return}P&&ot({v:st,ch:"ctl",type:"metrics",metrics:{...P,planeLink:Rg()}})}function t$(){i&&(_n&&clearInterval(_n),Og(),_n=setInterval(Og,c),_n.unref?.())}async function n$(){let P;try{P=await o()}catch(D){I.error("[plane-link] backfill listTracks failed:",D?.message||D);return}ot({v:st,ch:"ctl",type:"tracks",ids:P.filter(D=>!D.transferredTo).map(D=>D.id).filter(D=>typeof D=="string")});for(let D of P){if(!X)return;let x;try{x=await F(D)}catch{continue}let N=W[D.id]??0,q=x.filter(Ee=>Ee.seq>N&&typeof Ee.id=="string"),ye=$e==="http"?Ke:Ur;for(let Ee=0;Ee<q.length;Ee+=ye){if(!await XI(D.id))return;let Pe=q.slice(Ee,Ee+Math.min(ye,$e==="http"?Ke:Ur)).map(kt=>({trackId:D.id,event:kt}));if(!ya(Pe))return}}}function Cg(P){!X||!P?.trackId||typeof P.id!="string"||(m.push({trackId:P.trackId,event:P}),b||(b=setTimeout(Nd,RE),b.unref?.()))}let ka=new Map;async function r$(P){let{reqId:D,method:x,path:N,headers:q={},body:ye}=P,Ee=Object.fromEntries(Object.entries(q).filter(([,kt])=>kt!=null)),Pe=new AbortController;ka.set(D,Pe);try{if(P.host){await o$(P,Ee,Pe);return}let kt=await fetch(h+N,{method:x,headers:Ee,body:ye?Buffer.from(ye,"base64"):void 0,signal:Pe.signal}),Dt=Object.fromEntries(kt.headers);for(let Je of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Dt[Je];if(Dt["content-type"]||(Dt["content-type"]="application/octet-stream"),ot({v:st,ch:"control",type:"http_head",reqId:D,status:kt.status,headers:Dt}),kt.body){for await(let Je of kt.body)if(!ot({v:st,ch:"control",type:"http_data",reqId:D,data:Buffer.from(Je).toString("base64")}))break}ot({v:st,ch:"control",type:"http_end",reqId:D,error:null})}finally{ka.delete(D)}}function o$(P,D,x){let{reqId:N,method:q,path:ye,body:Ee}=P,Pe=new URL(h);return new Promise(kt=>{let Dt=BL.request({host:Pe.hostname,port:Pe.port,method:q,path:ye,headers:{...D,host:P.host},signal:x.signal},Je=>{let{connection:Ng,"keep-alive":xW,"transfer-encoding":vW,upgrade:SW,...s$}=Je.headers;ot({v:st,ch:"control",type:"http_head",reqId:N,status:Je.statusCode||502,headers:s$}),Je.on("data",a$=>{ot({v:st,ch:"control",type:"http_data",reqId:N,data:Buffer.from(a$).toString("base64")})||Je.destroy()}),Je.on("end",()=>{ot({v:st,ch:"control",type:"http_end",reqId:N,error:null}),kt()}),Je.on("error",()=>{ot({v:st,ch:"control",type:"http_end",reqId:N,error:"view upstream error"}),kt()})});Dt.on("error",Je=>{ot({v:st,ch:"control",type:"http_end",reqId:N,error:Je?.message||"view request failed"}),kt()}),Ee&&Dt.write(Buffer.from(Ee,"base64")),Dt.end()})}function Mg(){if(he)return;M=new mw(e,{headers:{authorization:`Bearer ${t}`}}),M.on("open",async()=>{let D=null;try{D=await s?.()||null}catch{D=null}ot({v:st,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:en,installKind:p,...typeof d=="function"?{updatePolicy:d()}:d?{updatePolicy:d}:{},providers:LL(),...D?{agents:D}:{}})}),M.on("message",D=>{let x;try{x=JSON.parse(D)}catch{return}if(x.ch==="ctl"&&x.type==="hello_ok"){X=!0,De=_E,Ye=null,I.log(`[plane-link] connected to plane as ${n}`),x.minNodeVersion&&en&&ac(en,x.minNodeVersion)&&I.error(`[plane-link] this node is bostrat-node ${en}; the plane asks for \u2265${x.minNodeVersion} \u2014 update with: ${Ku(p)}`);try{u?.(typeof x.updatePolicy=="string"?x.updatePolicy:null)}catch{}let N=typeof x.latestNodeVersion=="string"?x.latestNodeVersion:null;if(N&&en&&ac(en,N)&&!H.has(N)){H.add(N),I.log(`[plane-link] bostrat-node ${N} is available (this node runs ${en}) \u2014 ${Ku(p)}`);try{l?.({current:en,latest:N,min:x.minNodeVersion??null,installKind:p,required:!!(x.minNodeVersion&&ac(en,x.minNodeVersion))})}catch(ye){I.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}let q=x.ingest==="http";$e=k==="ws"?"ws":k==="http"||q?"http":"ws",k==="http"&&!q&&I.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),$e==="http"&&I.log("[plane-link] spine rides the HTTPS ingest door"),ze=!1,E=0,Ke=Ur,An&&clearInterval(An),An=setInterval(()=>{ot({v:st,ch:"ctl",type:"ping"}),$g(),e$().catch(()=>{}),Md(),m.length&&!b&&Nd()},R),An.unref?.(),t$(),n$().catch(ye=>I.error("[plane-link] backfill failed:",ye?.message||ye));return}if(x.ch==="ctl"&&x.type==="hello_refused"){let N=typeof x.minNodeVersion=="string"?x.minNodeVersion:null,q=typeof x.latestNodeVersion=="string"?x.latestNodeVersion:null;I.error(`[plane-link] plane REFUSED this node: bostrat-node ${en??"?"} is below its minimum ${N??"?"} \u2014 ${x.fix||Ku(p)}`),De=IE,Ye=0;try{f?.({current:en,min:N,latest:q,fix:x.fix||Ku(p),installKind:p})}catch(ye){I.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}return}if(x.ch==="ctl"&&x.type==="update_policy"){try{u?.(typeof x.mode=="string"?x.mode:null)}catch{}return}if(x.ch==="ctl"&&x.type==="usage_caps"){jl(x.caps??{},{source:"plane",updatedAt:x.updatedAt??Date.now()}).then(N=>I.log(`[plane-link] spend caps ${Object.keys(N.caps).length?`updated (${Object.keys(N.caps).join(", ")})`:"cleared"} from plane`)).catch(N=>I.error("[plane-link] spend-caps push rejected:",N?.message||N));return}if(x.ch==="ctl"&&x.type==="transferred"&&Array.isArray(x.tracks)){Promise.resolve().then(()=>a?.(x.tracks)).catch(N=>I.error("[plane-link] transfer notice failed:",N?.message||N));return}if(x.ch==="events"&&x.type==="ack"&&Array.isArray(x.ids)){Cd(x.ids);return}if(x.ch==="events"&&x.type==="nack"&&Array.isArray(x.ids)){_g(x.ids,x.reason,x.permanent===!0,x.retryAfterMs??null);return}if(x.ch==="control"&&x.type==="http_open"){r$(x).catch(N=>ot({v:st,ch:"control",type:"http_end",reqId:x.reqId,error:N?.message||"tunnel error"}));return}if(x.ch==="control"&&x.type==="http_cancel"){ka.get(x.reqId)?.abort();return}if(x.ch==="attach"&&x.type==="open"){let N=new mw(U+x.path,Array.isArray(x.protocols)?x.protocols:[],x.host?{headers:{host:x.host}}:void 0);ae.set(x.streamId,N),J.set(x.streamId,[]),N.on("open",()=>{let ye=J.get(x.streamId)||[];J.delete(x.streamId);for(let{buf:Ee,bin:Pe}of ye)N.send(Ee,{binary:Pe})}),N.on("message",(ye,Ee)=>{let Pe=Ee?ye:Buffer.from(String(ye));ot({v:st,ch:"attach",type:"data",streamId:x.streamId,data:Pe.toString("base64"),...Ee?{bin:!0}:{}})});let q=()=>{J.delete(x.streamId),ae.delete(x.streamId)&&ot({v:st,ch:"attach",type:"close",streamId:x.streamId})};N.on("close",q),N.on("error",q);return}if(x.ch==="attach"&&x.type==="data"){let N=ae.get(x.streamId);if(!N)return;let q=Buffer.from(x.data,"base64"),ye=x.bin===!0,Ee=J.get(x.streamId);Ee?Ee.push({buf:q,bin:ye}):N.send(q,{binary:ye});return}if(x.ch==="attach"&&x.type==="close"){let N=ae.get(x.streamId);ae.delete(x.streamId),J.delete(x.streamId),N?.close();return}});let P=()=>{X&&I.log("[plane-link] disconnected from plane"),X=!1,An&&(clearInterval(An),An=null),_n&&(clearInterval(_n),_n=null),se.clear(),be={},m=[],b&&(clearTimeout(b),b=null),y.clear();for(let N of ae.values())N.close();ae.clear(),J.clear();for(let N of ka.values())N.abort();if(ka.clear(),he||tt)return;Ye==null&&(Ye=Date.now());let D=Date.now()-Ye<FL,x=Math.max(D?UL+Math.random()*HL:De,ie-Date.now());tt=setTimeout(()=>{tt=null,Mg()},x),tt.unref?.(),D||(De=Math.min(De*2,IE))};M.on("close",P),M.on("error",D=>{let x=/Unexpected server response: (\d+)/.exec(D?.message||"")?.[1];(x==="401"||x==="403")&&Date.now()-Cc>=OE&&(Cc=Date.now(),I.error(`[plane-link] plane refused this node's credentials (HTTP ${x}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),P()})}return ct.on("event",Cg),zI().then(Mg),{stop:()=>{he=!0,ct.off("event",Cg),tt&&clearTimeout(tt),rr&&clearTimeout(rr),An&&clearInterval(An),_n&&clearInterval(_n),b&&clearTimeout(b),ot({v:st,ch:"ctl",type:"bye",reason:"shutdown"}),M?.close()},status:()=>({connected:X,nodeId:n,cursor:{...W},inflight:se.size,...Rg()}),_tick:()=>{$g(),Md()}}}fl();var Vu=(()=>{try{return QL(import.meta.url)("../package.json").version||null}catch{return null}})(),eF=process.env.BOSTRAT_STATE_DIR||at.join(process.env.HOME||XL.tmpdir(),".bostrat"),DE=at.join(eF,"self-update"),zu=at.join(DE,"status.json"),tF=1440*6e4,nF=6e4,ic=new Set(["npm-global","git-checkout"]);function BE({kind:e=Lo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:ic.has(e)?e==="git-checkout"||rF(t)?"auto":"prompt":"off"}function rF(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Nt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function oF({kind:e,to:t,from:n=Vu,statusFile:r=zu,logFile:o=at.join(DE,"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||at.resolve(at.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!ic.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let f=(g,k="")=>`write_status ${Nt(g)} ${k}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Nt(i)}`,p=`
|
|
555
594
|
health() {
|
|
556
595
|
for i in $(seq 1 45); do
|
|
557
596
|
sleep 1
|
|
558
597
|
if curl -sf -o /dev/null "http://127.0.0.1:${s}/api/health"; then return 0; fi
|
|
559
598
|
done
|
|
560
599
|
return 1
|
|
561
|
-
}`,
|
|
600
|
+
}`,h=`#!/usr/bin/env bash
|
|
562
601
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
563
602
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
564
603
|
set -u
|
|
565
|
-
FROM=${
|
|
566
|
-
TO=${
|
|
567
|
-
STATUS_FILE=${
|
|
604
|
+
FROM=${Nt(n??"")}
|
|
605
|
+
TO=${Nt(t)}
|
|
606
|
+
STATUS_FILE=${Nt(r)}
|
|
568
607
|
STARTED_AT=${Number(u)}
|
|
569
608
|
write_status() { # <status> [error]
|
|
570
609
|
local err="\${2:-}"
|
|
571
610
|
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
572
611
|
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
573
|
-
"$1" "$FROM" "$TO" ${
|
|
612
|
+
"$1" "$FROM" "$TO" ${Nt(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
574
613
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
575
614
|
}
|
|
576
615
|
${p}
|
|
577
|
-
`;if(e==="npm-global"){let
|
|
578
|
-
`:"",
|
|
579
|
-
${
|
|
580
|
-
if ! ${
|
|
581
|
-
${
|
|
616
|
+
`;if(e==="npm-global"){let g=a?at.join(a,"bin","npm"):"npm",k=a?`export PATH=${Nt(at.join(a,"bin"))}:"$PATH"
|
|
617
|
+
`:"",S=a?` --prefix ${Nt(a)}`:"",A=R=>`${Nt(g)} install -g "bostrat-node@${R}" --no-audit --no-fund${S}`;return h+k+`
|
|
618
|
+
${f("updating",'"" ')}
|
|
619
|
+
if ! ${A("$TO")}; then
|
|
620
|
+
${f("failed",'"npm install failed"')}
|
|
582
621
|
exit 1
|
|
583
622
|
fi
|
|
584
|
-
${
|
|
585
|
-
if ! ${
|
|
586
|
-
${
|
|
623
|
+
${f("restarting")}
|
|
624
|
+
if ! ${d}; then
|
|
625
|
+
${f("failed",'"service restart failed"')}
|
|
587
626
|
exit 1
|
|
588
627
|
fi
|
|
589
628
|
if health; then
|
|
@@ -591,89 +630,89 @@ if health; then
|
|
|
591
630
|
exit 0
|
|
592
631
|
fi
|
|
593
632
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
594
|
-
${
|
|
595
|
-
if [ -n "$FROM" ] && ${
|
|
596
|
-
${
|
|
633
|
+
${f("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
634
|
+
if [ -n "$FROM" ] && ${A("$FROM")} && ${d} && health; then
|
|
635
|
+
${f("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
597
636
|
exit 1
|
|
598
637
|
fi
|
|
599
|
-
${
|
|
638
|
+
${f("failed",'"agent did not answer after the update AND the rollback"')}
|
|
600
639
|
exit 1
|
|
601
|
-
`}let w=
|
|
602
|
-
LIVE=${
|
|
640
|
+
`}let w=g=>`( cd ${Nt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return h+`
|
|
641
|
+
LIVE=${Nt(l)}
|
|
603
642
|
TMP_SCRIPT="$(mktemp)"
|
|
604
643
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
605
644
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
606
645
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
607
646
|
# and nothing here may write after that boot or the settle would repeat.
|
|
608
|
-
${
|
|
647
|
+
${f("restarting")}
|
|
609
648
|
if ${w("$TO")}; then
|
|
610
649
|
exit 0
|
|
611
650
|
fi
|
|
612
|
-
${
|
|
651
|
+
${f("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
613
652
|
if [ -n "$FROM" ] && ${w("$FROM")}; then
|
|
614
|
-
${
|
|
653
|
+
${f("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
615
654
|
exit 1
|
|
616
655
|
fi
|
|
617
|
-
${
|
|
656
|
+
${f("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
618
657
|
exit 1
|
|
619
|
-
`}async function tc(e=Bu){try{return JSON.parse(await Pr.readFile(e,"utf8"))}catch{return null}}var pL=new Set(["updating","restarting","rolling-back"]),ju=e=>!!(e&&pL.has(e.status));async function Lu({to:e,from:t=Du,kind:n=_o(),reason:r="manual",statusFile:o=Bu,spawnImpl:s=aL,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!ec.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!Qi(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await tc(o);if(ju(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await Pr.mkdir(tt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?xf(await mL())||tt.resolve(tt.dirname(process.execPath),".."):null,d=fL({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),f=tt.join(tt.dirname(o),`update-${e}.sh`),p=tt.join(tt.dirname(o),"last.log");await Pr.writeFile(f,d,{mode:448}),await Pr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let m=rA.existsSync("/run/systemd/system")&&await hL("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",f]]:["setsid",["-f","bash","-c",`exec bash ${_t(f)} >> ${_t(p)} 2>&1`]];try{s(m[0],m[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await Pr.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 mL(){try{return await Pr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function hL(e){for(let t of String(process.env.PATH||"").split(tt.delimiter))try{return await Pr.access(tt.join(t,e),rA.constants.X_OK),!0}catch{}return!1}var wL=45*6e4;async function aA({statusFile:e=Bu,current:t=Du,now:n=Date.now,staleMs:r=wL}={}){let o=await tc(e);if(!o)return null;let s=a=>Pr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(ju(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function iA({isBusy:e,start:t=Lu,onStuck:n=null,onStarted:r=null,tickMs:o=uL,stuckMs:s=lL,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},f=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let m=u;d();let w=await t({to:m.to,from:m.from,kind:m.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...m,result:w})}catch{}};return{arm({to:p,from:m=Du,kind:w=_o()}){return u?.to===p?!1:(d(),u={to:p,from:m,kind:w,since:a(),stuckFired:!1,timer:i(f,o)},u.timer.unref?.(),!0)},tick:f,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ue();it();var gL=(...e)=>fetch(...e);function yL(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ew(e=process.env){if(!yL(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 gL(`${$e(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function Xs(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await ew(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 Fu(e,t,{source:n="chat"}={}){return O(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ue();it();var bL=(...e)=>fetch(...e);async function Uu(e,t,n,r=process.env){if(!($e(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 bL(`${$e(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 kL(e,t=process.env){return Uu("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function cA({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await kL(t,o):await Xs(e,o),a=await Uu("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function tw(e,t=process.env){let n=await Xs(e,t),r=await Uu("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function nw(e,t=process.env){return(await Uu("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function lA(e,t,n,r=process.env){let{blueprint:o,items:s}=await tw(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 O(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ue();it();var xL=(...e)=>fetch(...e);async function vL(e,t,n,r=process.env){if(!($e(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 xL(`${$e(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 uA=72,rw=160;async function dA(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>uA)throw new Error(`the title is the choice as a LABEL \u2014 ${uA} 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 ${rw} chars) a reader can act on without opening the record`);if(c.length>rw)throw new Error(`the summary is one sentence \u2014 ${rw} 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 O(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 fA(e,t=process.env){return vL("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Gm();ue();import ic from"node:fs/promises";import Yu from"node:path";import fF from"node:os";rr();import SL from"node:fs/promises";import pA from"node:path";import TL from"node:os";var mA=()=>pA.join(process.env.BOSTRAT_STATE_DIR||pA.join(process.env.HOME||TL.tmpdir(),".bostrat"),"integrations.json");async function Xn(){try{return JSON.parse(await SL.readFile(mA(),"utf8"))||{}}catch{return{}}}var Hu=["linked","prOpened","prMerged","create"],ow=["prompt","auto","off"],AL={create:["prompt","off"]},hA=e=>AL[e]??ow;function Wu(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Hu){let s=hA(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 Io(){let e=await Xn(),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:Wu(e),defaultTeam:n}:null}async function qu(){let e=await Xn(),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:Wu(e)}}async function sw(){return Wu(await Xn())}async function wA(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Hu.join(", ")}`);for(let[r,o]of t){if(!Hu.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Hu.join(", ")}`);let s=hA(r);if(!s.includes(o))throw new Error(s.length<ow.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${ow.join(", ")}`)}let n=await Xn();return n.ticketFlow={...Wu(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await wt(mA(),n,{mode:384}),n.ticketFlow}async function nc(){let e=(await Xn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function EL(){let e=(await Xn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var _L={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Ku(){let t=(await Xn()).voiceChat||{},n=t.provider||"openai",r=_L[n];if(!r)return null;let o=n==="openai"?await EL():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function gA(){let e=(await Xn()).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 $o(){let e=(await Xn()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}ue();sr();gr();js();import oc from"node:fs/promises";import EA from"node:path";import ZL from"node:os";import{randomUUID as _A}from"node:crypto";var iw={};Ad(iw,{createComment:()=>DL,findIssueUrl:()=>OL,issueStates:()=>LL,mdToAdf:()=>kA,moveIssue:()=>FL,parseIssueRef:()=>RL,resolveIssue:()=>CL,uploadImage:()=>BL,upsertCard:()=>ML,whoami:()=>aw});var Zs=e=>String(e.baseUrl||"").replace(/\/$/,""),bA=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ro(e,t,n,r){let o=await fetch(`${Zs(e)}${n}`,{method:t,headers:{authorization:bA(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 IL=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,$L=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function RL(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(IL);return r?r[1].toUpperCase():null}let n=t.match($L);return n?n[1]:null}function OL(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 CL(e,t){let n;try{n=await Ro(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:`${Zs(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function ML(e,t,{url:n,title:r,subtitle:o}){await Ro(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 NL=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function PL(e){let t=[],n=0;for(let r of e.matchAll(NL)){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 kA(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
620
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
658
|
+
`}async function cc(e=zu){try{return JSON.parse(await Hr.readFile(e,"utf8"))}catch{return null}}var sF=new Set(["updating","restarting","rolling-back"]),Yu=e=>!!(e&&sF.has(e.status));async function Ju({to:e,from:t=Vu,kind:n=Lo(),reason:r="manual",statusFile:o=zu,spawnImpl:s=ZL,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!ic.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!ac(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await cc(o);if(Yu(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await Hr.mkdir(at.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Of(await aF())||at.resolve(at.dirname(process.execPath),".."):null,f=oF({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=at.join(at.dirname(o),`update-${e}.sh`),p=at.join(at.dirname(o),"last.log");await Hr.writeFile(d,f,{mode:448}),await Hr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let h=PE.existsSync("/run/systemd/system")&&await iF("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${Nt(d)} >> ${Nt(p)} 2>&1`]];try{s(h[0],h[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await Hr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${w?.message||w}`})),{ok:!1,reason:`could not spawn updater: ${w?.message||w}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:d,statusFile:o}}async function aF(){try{return await Hr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function iF(e){for(let t of String(process.env.PATH||"").split(at.delimiter))try{return await Hr.access(at.join(t,e),PE.constants.X_OK),!0}catch{}return!1}var cF=45*6e4;async function jE({statusFile:e=zu,current:t=Vu,now:n=Date.now,staleMs:r=cF}={}){let o=await cc(e);if(!o)return null;let s=a=>Hr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Yu(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 LE({isBusy:e,start:t=Ju,onStuck:n=null,onStarted:r=null,tickMs:o=nF,stuckMs:s=tF,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=Vu,kind:w=Lo()}){return u?.to===p?!1:(f(),u={to:p,from:h,kind:w,since:a(),stuckFired:!1,timer:i(d,o)},u.timer.unref?.(),!0)},tick:d,cancel:f,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}de();it();var lF=(...e)=>fetch(...e);function uF(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ww(e=process.env){if(!uF(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 lF(`${_e(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function oa(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await ww(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 Xu(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}})}de();it();var dF=(...e)=>fetch(...e);async function Zu(e,t,n,r=process.env){if(!(_e(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 dF(`${_e(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 fF(e,t=process.env){return Zu("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function FE({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await fF(t,o):await oa(e,o),a=await Zu("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function gw(e,t=process.env){let n=await oa(e,t),r=await Zu("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function yw(e,t=process.env){return(await Zu("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function UE(e,t,n,r=process.env){let{blueprint:o,items:s}=await gw(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}}de();it();var pF=(...e)=>fetch(...e);async function mF(e,t,n,r=process.env){if(!(_e(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 pF(`${_e(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 HE=72,bw=160;async function WE(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>HE)throw new Error(`the title is the choice as a LABEL \u2014 ${HE} 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 ${bw} chars) a reader can act on without opening the record`);if(c.length>bw)throw new Error(`the summary is one sentence \u2014 ${bw} 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 qE(e,t=process.env){return mF("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}ch();de();import mc from"node:fs/promises";import id from"node:path";import oU from"node:os";cr();import hF from"node:fs/promises";import KE from"node:path";import wF from"node:os";var GE=()=>KE.join(process.env.BOSTRAT_STATE_DIR||KE.join(process.env.HOME||wF.tmpdir(),".bostrat"),"integrations.json");async function tr(){try{return JSON.parse(await hF.readFile(GE(),"utf8"))||{}}catch{return{}}}var Qu=["linked","prOpened","prMerged","create"],kw=["prompt","auto","off"],gF={create:["prompt","off"]},VE=e=>gF[e]??kw;function ed(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Qu){let s=VE(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function Fo(){let e=await tr(),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:ed(e),defaultTeam:n}:null}async function td(){let e=await tr(),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:ed(e)}}async function xw(){return ed(await tr())}async function zE(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Qu.join(", ")}`);for(let[r,o]of t){if(!Qu.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Qu.join(", ")}`);let s=VE(r);if(!s.includes(o))throw new Error(s.length<kw.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${kw.join(", ")}`)}let n=await tr();return n.ticketFlow={...ed(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await vt(GE(),n,{mode:384}),n.ticketFlow}async function lc(){let e=(await tr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function yF(){let e=(await tr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var bF={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function nd(){let t=(await tr()).voiceChat||{},n=t.provider||"openai",r=bF[n];if(!r)return null;let o=n==="openai"?await yF():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function YE(){let e=(await tr()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function Uo(){let e=(await tr()).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}}de();ur();vr();Ks();import dc from"node:fs/promises";import oA from"node:path";import qF from"node:os";import{randomUUID as sA}from"node:crypto";var Sw={};Pd(Sw,{createComment:()=>IF,findIssueUrl:()=>SF,issueStates:()=>OF,mdToAdf:()=>ZE,moveIssue:()=>CF,parseIssueRef:()=>vF,resolveIssue:()=>TF,uploadImage:()=>$F,upsertCard:()=>EF,whoami:()=>vw});var sa=e=>String(e.baseUrl||"").replace(/\/$/,""),XE=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ho(e,t,n,r){let o=await fetch(`${sa(e)}${n}`,{method:t,headers:{authorization:XE(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 kF=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,xF=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function vF(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(kF);return r?r[1].toUpperCase():null}let n=t.match(xF);return n?n[1]:null}function SF(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 TF(e,t){let n;try{n=await Ho(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${sa(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function EF(e,t,{url:n,title:r,subtitle:o}){await Ho(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var AF=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function _F(e){let t=[],n=0;for(let r of e.matchAll(AF)){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 ZE(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
659
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(..._F(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 IF(e,t,n){let r=await Ho(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:ZE(n)});return r?.id?`${sa(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function vw(e){let t=await Ho(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(sa(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function $F(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(`${sa(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:XE(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??`${sa(e)}/rest/api/3/attachment/content/${c.id}`}var RF={new:"unstarted",indeterminate:"started",done:"completed"},JE=e=>RF[e?.key]??"started";async function OF(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Ho(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Ho(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:JE(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:JE(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function CF(e,t,n){await Ho(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var _w={};Pd(_w,{createComment:()=>FF,createIssue:()=>Aw,createProject:()=>Ew,findIssueUrl:()=>BF,issueStates:()=>HF,moveIssue:()=>WF,parseIssueRef:()=>DF,resolveIssue:()=>jF,teams:()=>aa,uploadImage:()=>UF,upsertCard:()=>LF,whoami:()=>Tw});var MF=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Tn(e,t,n={}){let r=await fetch(MF(),{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 NF=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,PF=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function DF(e){let t=String(e||"").trim(),n=t.match(NF);if(n)return n[1].toUpperCase();let r=t.match(PF);return r?r[1]:null}function BF(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 jF(e,t){let r=(await Tn(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 LF(e,t,{url:n,title:r,subtitle:o}){let s=await Tn(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 FF(e,t,n){let r=await Tn(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 Tw(e){let t=await Tn(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 UF(e,{filename:t,contentType:n,buffer:r}){let o=await Tn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
621
660
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
622
661
|
success
|
|
623
662
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
624
663
|
}
|
|
625
|
-
}`,{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 YL(e,t){let n=await kn(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 XL(e,t,n){if(!(await kn(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 Qs(e){return((await kn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var xA=255;async function lw(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>xA,a=s?`${o.slice(0,xA-1).trimEnd()}\u2026`:o,i=await kn(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 uw(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await kn(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 IA={linear:{api:dw,config:Io},jira:{api:iw,config:qu}};async function yw(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(IA)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Oo(e){let t=IA[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function bw(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 $A=()=>process.env.BOSTRAT_STATE_DIR||EA.join(process.env.HOME||ZL.tmpdir(),".bostrat"),hw=()=>EA.join($A(),"tickets.json"),RA=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Gu=e=>`${RA()}/track/${e}`,Dr=null,fw=null,pw=Promise.resolve();async function ac(){return Dr||(fw??=oc.readFile(hw(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Dr=e,!t&&Me()){let{records:n,source:r}=await Ye("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;Dr[s]=i}Object.keys(Dr).length&&await ww().catch(()=>{})}}return Dr}).finally(()=>fw=null),fw)}async function ww(){let e=JSON.stringify({tickets:Dr},null,2),t=`${hw()}.${process.pid}.tmp`;await oc.mkdir($A(),{recursive:!0}),await oc.writeFile(t,e),await oc.rename(t,hw())}async function Br(e=null){if(pw=pw.then(ww,ww),await pw.catch(()=>{}),!e)return;let t=Dr?.[e];t?ct("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Ft("tickets",e).catch(()=>{})}async function xn(e){return(await ac())[e]??null}async function OA(){return{...await ac()}}async function CA(){return(await yw()).length>0}async function ea(e,t,{source:n="api"}={}){let r=await yw();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 ac())[e.id]=c,await Br(e.id),await O({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 sc(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 rc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await PA(e.id,c,"linked"),c}async function kw(e){let t=await ac();return t[e]?(delete t[e],await Br(e),!0):!1}var gw=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function MA(e,t){if(!e.track?.cwd)return null;let n=wr(await F({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function QL(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${gw(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${gw(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function eF(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(gw(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function sc(e,t){let n=await Oo(t);if(!n)return;let r=await MA(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Gu(e),title:`Bostrat Track: ${t.track.name}`,subtitle:eF(t,r)})}function Vu(e){let t=new URL(RA());return`${t.protocol}//${e}.${t.host}/`}var tF={linked:"linked",opened:"prOpened",merged:"prMerged"};function nF(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 NA(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Br(e),await O({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 PA(e,t,n){let r=await Oo(t);if(!r)return;let o=r.cfg.ticketFlow?.[tF[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=bw(s,a),c=nF(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 NA(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await O({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:_A(),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 DA(e){let t=await xn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Oo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=bw(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Br(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 xw(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await xn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Oo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=bw(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 Br(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await NA(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function BA(e,t){let n=await xn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Oo(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 O({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 jA(e,t,n){if(!t)throw new Error("offerId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await xw(e.id,o.payload.to?.id,{mode:"offer"})),await O({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 vA=50;async function LA(){let e=await Io();if(!e)throw new Error("no ticketing integration configured");return Qs(e)}function rF(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>vA)throw new Error(`too many issues (${n.length}); cap is ${vA} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function oF(e,t,n,r){let o=r??await Qs(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 xn(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 FA(e,t){let n=await Io();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=rF(t),o=await Qs(n),s=await oF(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=_A();return await O({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 UA(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await F({id:e.id,cwd:e.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await O({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 Io();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Qs(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await lw(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let w=await uw(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await O({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 O({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&f.length===1&&!await xn(e.id)&&(p=await ea(e,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function rc(e,t){let n=await Oo(e);n&&await n.api.createComment(n.cfg,e,t)}async function sF(e,t,n){let r=await Oo(e);if(!r)return null;let o=await qn(t);if(!o)return null;let s=zt(o,n);if(!s)return null;let a=await oc.readFile(Nt(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 SA=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),mw=new Map;function aF(e,t){if(SA()<=0)return sc(e,t);if(mw.has(e))return;let n=setTimeout(()=>{mw.delete(e),xn(e).then(r=>r?sc(e,r):null).catch(()=>{})},SA());n.unref?.(),mw.set(e,n)}var TA=new Map;async function AA(e,t,n){let r=TA.get(e)??0;Date.now()-r<600*1e3||(TA.set(e,Date.now()),await O({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function iF(e,{resolveTrack:t=null}={}){let n=await xn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await yw())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 ea(s,o,{source:"chat"})}catch(a){await AA(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 Br(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Br(e.trackId),await sc(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 Br(e.trackId),await sc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await MA(n,e.trackId).catch(()=>null):null;await rc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
664
|
+
}`,{contentType:n,filename:t,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function HF(e,t){let n=await Tn(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 WF(e,t,n){if(!(await Tn(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function aa(e){return((await Tn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var QE=255;async function Ew(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>QE,a=s?`${o.slice(0,QE-1).trimEnd()}\u2026`:o,i=await Tn(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 Aw(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Tn(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 aA={linear:{api:_w,config:Fo},jira:{api:Sw,config:td}};async function Nw(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(aA)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Wo(e){let t=aA[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Pw(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 iA=()=>process.env.BOSTRAT_STATE_DIR||oA.join(process.env.HOME||qF.tmpdir(),".bostrat"),Ow=()=>oA.join(iA(),"tickets.json"),cA=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),rd=e=>`${cA()}/track/${e}`,Wr=null,Iw=null,$w=Promise.resolve();async function pc(){return Wr||(Iw??=dc.readFile(Ow(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Wr=e,!t&&Me()){let{records:n,source:r}=await nt("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;Wr[s]=i}Object.keys(Wr).length&&await Cw().catch(()=>{})}}return Wr}).finally(()=>Iw=null),Iw)}async function Cw(){let e=JSON.stringify({tickets:Wr},null,2),t=`${Ow()}.${process.pid}.tmp`;await dc.mkdir(iA(),{recursive:!0}),await dc.writeFile(t,e),await dc.rename(t,Ow())}async function qr(e=null){if($w=$w.then(Cw,Cw),await $w.catch(()=>{}),!e)return;let t=Wr?.[e];t?ht("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):qt("tickets",e).catch(()=>{})}async function En(e){return(await pc())[e]??null}async function lA(){return{...await pc()}}async function uA(){return(await Nw()).length>0}async function ia(e,t,{source:n="api"}={}){let r=await Nw();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 pc())[e.id]=c,await qr(e.id),await $({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await fc(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 uc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await pA(e.id,c,"linked"),c}async function Dw(e){let t=await pc();return t[e]?(delete t[e],await qr(e),!0):!1}var Mw=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function dA(e,t){if(!e.track?.cwd)return null;let n=xr(await F({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function KF(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Mw(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Mw(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function GF(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(Mw(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function fc(e,t){let n=await Wo(t);if(!n)return;let r=await dA(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:rd(e),title:`Bostrat Track: ${t.track.name}`,subtitle:GF(t,r)})}function od(e){let t=new URL(cA());return`${t.protocol}//${e}.${t.host}/`}var VF={linked:"linked",opened:"prOpened",merged:"prMerged"};function zF(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 fA(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await qr(e),await $({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function pA(e,t,n){let r=await Wo(t);if(!r)return;let o=r.cfg.ticketFlow?.[VF[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Pw(s,a),c=zF(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 fA(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:sA(),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 mA(e){let t=await En(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Wo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Pw(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await qr(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function Bw(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await En(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Wo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Pw(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await qr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await fA(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function hA(e,t){let n=await En(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Wo(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await $({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function wA(e,t,n){if(!t)throw new Error("offerId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await Bw(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 eA=50;async function gA(){let e=await Fo();if(!e)throw new Error("no ticketing integration configured");return aa(e)}function YF(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>eA)throw new Error(`too many issues (${n.length}); cap is ${eA} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function JF(e,t,n,r){let o=r??await aa(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await En(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 yA(e,t){let n=await Fo();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=YF(t),o=await aa(n),s=await JF(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=sA();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 bA(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await F({id:e.id,cwd:e.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await Fo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await aa(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let h=await Ew(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 Aw(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 En(e.id)&&(p=await ia(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function uc(e,t){let n=await Wo(e);n&&await n.api.createComment(n.cfg,e,t)}async function XF(e,t,n){let r=await Wo(e);if(!r)return null;let o=await zn(t);if(!o)return null;let s=Zt(o,n);if(!s)return null;let a=await dc.readFile(Ft(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 tA=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Rw=new Map;function ZF(e,t){if(tA()<=0)return fc(e,t);if(Rw.has(e))return;let n=setTimeout(()=>{Rw.delete(e),En(e).then(r=>r?fc(e,r):null).catch(()=>{})},tA());n.unref?.(),Rw.set(e,n)}var nA=new Map;async function rA(e,t,n){let r=nA.get(e)??0;Date.now()-r<600*1e3||(nA.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 QF(e,{resolveTrack:t=null}={}){let n=await En(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Nw())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=t?await t(e.trackId):null;if(!s)return;try{await ia(s,o,{source:"chat"})}catch(a){await rA(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await qr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await qr(e.trackId),await fc(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await qr(e.trackId),await fc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await dA(n,e.trackId).catch(()=>null):null;await uc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
626
665
|
|
|
627
|
-
Agent cost for this Track: ${
|
|
666
|
+
Agent cost for this Track: ${KF(o)}.`:"")),await pA(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await XF(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await uc(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
628
667
|
|
|
629
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
630
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
631
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
668
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await uc(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 uc(n,`**Bostrat** \u2014 live preview **${r.name}**: ${od(r.slug)}
|
|
669
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await ZF(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await rA(e.trackId,s?.cwd??null,o)}}function kA({resolveTrack:e}){ct.on("event",t=>{QF(t,{resolveTrack:e}).catch(()=>{})})}async function xA(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await nt("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await pc(),r=0;for(let[o,s]of Object.entries(n))await ht("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}bo();Ks();import eU from"ws";var vA=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function sd(e,t,n={}){let r=await fetch(`${vA()}/${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 jw=(e,t)=>sd("chat.postMessage",e,t),qo=(e,t)=>sd("chat.update",e,t);async function yt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function tU(e,t,n={}){let r=await fetch(`${vA()}/${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 SA(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await tU("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 sd("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var nU=2200;function TA({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 sd("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new eU(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},p=!1,h=setTimeout(()=>{p=!0,d()},nU);Promise.resolve().then(()=>t(f)).then(w=>{clearTimeout(h),!p&&(p=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(h),p||(p=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var _A=()=>process.env.BOSTRAT_STATE_DIR||id.join(process.env.HOME||oU.tmpdir(),".bostrat"),Ww=()=>id.join(_A(),"slack.json"),ad=null,Lw=null,Fw=Promise.resolve();async function Kw(){return ad||(Lw??=mc.readFile(Ww(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>ad=e).finally(()=>Lw=null),Lw)}async function EA(){let e=JSON.stringify({threads:ad},null,2),t=`${Ww()}.${process.pid}.tmp`;await mc.mkdir(_A(),{recursive:!0}),await mc.writeFile(t,e),await mc.rename(t,Ww())}async function nn(){Fw=Fw.then(EA,EA),await Fw.catch(()=>{})}async function Gw(e){return(await Kw())[e]??null}async function sU(e,t){let n=await Kw();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var ke=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function hc(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function aU(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return hc(n.replace(/\n+/g," "),t)}function IA(e){let t=`*${ke(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${ke(e.repoLabel)} \xB7 \`${ke(e.branch||"?")}\` \xB7 lead *${ke(e.lead)}*`,r=e.kickoff?`> ${ke(hc(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
670
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${rd(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function qw(e,t){await qo(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:IA(t)})}async function Gr(e,t,n,r=void 0){return jw(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var AA=new Set;function Kr(e){AA.add(e),e.catch(()=>{}).finally(()=>AA.delete(e))}var Ce=e=>({response_type:"ephemeral",text:e}),iU="*/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._",cU=["claude","cursor","codex"];function Uw(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 Hw=(e,t)=>Ce(`No live Track matches \u201C${ke(t)}\u201D. Live: ${e.map(n=>`\`${ke(n.name)}\``).join(" \xB7 ")||"none"}`);async function $A(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=t.maxConcurrent){await yt(o,{...Ce(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await yt(o,{...Ce("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?id.basename(c.base):"fresh workspace"),lead:"claude"},u=await jw(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:IA(l),unfurl_links:!1});l.ts=u.ts,(await Kw())[i.id]=l,await nn(),await yt(o,{...Ce(`\u2713 *${ke(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await yt(o,{...Ce(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function lU(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Gw(n.id);s&&(s.archivedAt=Date.now(),await nn(),await qw(t,s).catch(()=>{})),await yt(r,{...Ce(`\u2713 *${ke(n.name)}* archived.`),replace_original:!0})}catch(s){await yt(r,{...Ce(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function uU(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await yt(o,{...Ce(`\u2713 Handing *${ke(n.name)}* to ${ke(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await yt(o,{...Ce(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function dU(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 yt(r,{...Ce(`\u2713 Stopped *${ke(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 yt(r,{...Ce(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function fU(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:hc(`${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 *${ke(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function pU(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?id.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 *${ke(s.name)}* \u2014 ${ke(i)}${a.branch?` \`${ke(a.branch)}\``:""} \xB7 lead ${ke(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
632
671
|
`)+(o?`
|
|
633
|
-
_${o} archived_`:"")}async function
|
|
634
|
-
\u2713 *${
|
|
635
|
-
\u2713 _answered_`}}]})}async function
|
|
636
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
637
|
-
`)){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 HF(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 WF=new Set(["Write","Edit","NotebookEdit"]),qF=220,KF=160;function GF(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(WF.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,qF)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Jr(o.command.trim().replace(/\s+/g," ")).slice(0,KF));return r}function VF(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Wt(t.cwd,t.sessionId):t?.cwd?af(t.cwd):null}async function QA(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 O(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=Zl(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:GF(e,o)});break;case"Stop":await zF(r,o,e,i);break;default:break}return a}async function zF(e,t,n,r){let o=[],s=VF(n,t);if(s)try{o=UF(await FF.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=HF(await F(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=`${d.model||"unknown"}\0${d.speed||""}`,p=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=d.tokens[m];d.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=d.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=d.cacheWriteTtl.h1),p.messageIds.push(d.messageId),p.speed=d.speed||null,u.set(f,p)}for(let[d,f]of u){let p=d.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:an(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}})}Lo();Zd();ue();fl();su();import{execFile as JF}from"node:child_process";import{promisify as YF}from"node:util";var XF=YF(JF),Ow=(e,t)=>XF("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4});function eE(e,t=Date.now()){return Bd(e,t)??0}function ZF(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 tE(e,t,{now:n=Date.now()}={}){let r=eE(t,n),o=await F(e);return ou({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:ZF(o,t.name,r)})}function QF(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=null;for(let c=n.length-1;c>=0;c--){let l=n[c];if(l.type==="turn.user"&&l.boxId===t.name&&l.actor==="user"){i=Rn(l.payload?.promptHead||l.payload?.text||"")?.category??null;break}}return{wallReason:a,fixerCategory:i}}async function eU(e,t){try{let{stdout:n}=await Ow(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 tU(e){try{let{stdout:t}=await Ow(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function nE(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await F(e);if(a.some(p=>p.type==="code.committed"&&p.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:p}=await Ow(r,["show","-s","--format=%ct",n]),m=Number(p.trim())*1e3;Number.isFinite(m)&&m>0&&(i=m)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=eE(t,i),l=await eU(r,n),u=await O(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:await tU(r),...l,ts:i}}),d=null,f=!1;try{let p=await Dv(r,n,{fixKey:Ns({trackId:e.id,boxId:t.name,epoch:c}),window:$r,context:{...QF(e,t,a,c),provider:t.agent??null}});if(p){d=await O(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:u.id,payload:p});let m=Bv(p);if(m>=Pv){let w=p.origins.filter(y=>y.class==="rework").slice(0,3);await O(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:{sha:n,subject:p.subject,linesReworked:m,origins:w.map(y=>({key:y.key,provider:y.provider,model:y.model,lines:y.lines,ageDays:y.ageDays})),heuristic:p.heuristic,ts:i}}),f=!0}}}catch(p){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${p?.message||p}`)}return{recorded:!0,event:u,rework:d,offered:f}}async function rE(e,t,n){if(!xi.includes(n))throw new Error(`bad cause "${n}" (${xi.join("|")})`);let r=await F(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(r.some(s=>s.type==="rework.cause.answered"&&s.payload?.sha===t))throw new Error("already answered");return O(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,cause:n,level:"B",ts:Date.now()}})}ue();ue();gr();ue();gr();On();function lc(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 oE=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),sE=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Xu=e=>Math.round(e*1e4)/1e4;async function Cw(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=oE(),d=0,f=[];for(let p of e||[]){let m=await F(p),w=wr(m,{boxLabels:lc(p)});if(w.turns===0){p.archivedAt?d++:f.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);sE(u,w.totalTokens);for(let[y,x]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:oE()},n[y].usd=Xu(n[y].usd+x.usd),n[y].events+=x.events,sE(n[y].tokens,x.tokens);for(let[y,x]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=Xu(r[y].usd+x.usd),r[y].events+=x.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:ls(),fleet:{totalUsd:Xu(o),asRecordedUsd:Xu(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 Fr=e=>`$${e.toFixed(4)}`,ta=(e,t)=>String(e).padEnd(t);function aE(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 ${Fr(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 ${Fr(t.asRecordedUsd)} \xB7 current-rates total ${Fr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Fr(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(` ${ta(a,26)} ${ta(Fr(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(` ${ta(a.name||a.id,30)} ${Fr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ta(c.label,40)} ${ta(Fr(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))}${ta(Fr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
638
|
-
`)}ue();Np();import aU from"node:fs/promises";import iU from"node:path";fl();gr();var Zu=e=>e.payload?.ts??e.ts,nU=12e4,Ur=e=>Math.round(e*1e4)/1e4,rU=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function oU(e,{since:t}){let{track:n,events:r,brief:o}=e,s=T=>Zu(T)>=t,a=Rn([Xa(n?.name),Xa(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let T of r)if(T.type==="subtask.delegated"&&T.boxId&&T.payload?.task){let I=Rn(T.payload.task);I&&i.set(T.boxId,I)}let c=new Map,l=T=>{let I=T||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=T=>({textCat:T,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((T,I)=>Zu(T)-Zu(I)),f=0,p=0,m=0,w={turns:0};for(let T of d){let I=l(T.boxId),$=I.segments[I.segments.length-1],D=Zu(T);switch(I.lastTs!=null&&D>I.lastTs&&s(T)&&($.activeMs+=Math.min(D-I.lastTs,nU)),I.lastTs=D,T.provider&&(I.provider=T.provider),T.type){case"turn.user":{let S=T.payload?.text;if(T.source==="hook"||!String(S||"").trim())break;let A=u(Rn(S));I.segments.push(A),s(T)&&(A.turns+=1);break}case"peer.message":{let S=T.payload?.text;if(!String(S||"").trim())break;let A=u(Rn(S));I.segments.push(A),s(T)&&(A.turns+=1);break}case"tool.called":{if(T.source==="transcript")break;let S=T.payload?.tool;S&&($.tools[S]=($.tools[S]||0)+1),s(T)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(T))break;let{usd:S,tokens:A,model:R,unpriced:E}=Ot(T);E&&(w.turns+=1),$.usd+=S,A&&($.tokens.input+=A.input,$.tokens.output+=A.output,$.tokens.cacheRead+=A.cacheRead,$.tokens.cacheWrite+=A.cacheWrite),$.models[R]=($.models[R]||0)+S,T.provider&&($.provider=T.provider);break}case"handoff.done":s(T)&&(f+=1);break;case"subtask.merged":s(T)&&(p+=1);break;case"error":s(T)&&T.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,x=!1;for(let[T,I]of c)for(let $ of I.segments){if(!($.usd>0||$.activeMs>0||$.turns>0||$.toolCalls>0))continue;let S=$.textCat?.category??i.get(T)?.category??Ab($.tools)?.category??a?.category??"uncategorized";y.has(S)||y.set(S,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let A=y.get(S);A.usd+=$.usd,A.activeMs+=$.activeMs,A.turns+=$.turns,A.toolCalls+=$.toolCalls;for(let[E,L]of Object.entries($.models))A.models[E]=(A.models[E]||0)+L;let R=$.provider||I.provider;R&&(A.providers[R]=(A.providers[R]||0)+$.usd),R==="cursor"&&$.usd===0&&(x=!0)}return{perCategory:y,handoffs:f,merged:p,failed:m,sawCursorActivity:x,unpricedTurns:w.turns}}function cE(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let w of e||[]){let{track:y,events:x}=w;if(!x?.length){a+=1;continue}let{perCategory:T,handoffs:I,merged:$,failed:D,sawCursorActivity:S,unpricedTurns:A}=oU(w,{since:r});S&&(i=!0),c+=A;let R={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:I,subtasksMerged:$,subtasksFailed:D};for(let[E,L]of T){R.usd+=L.usd,R.activeMs+=L.activeMs,R.turns+=L.turns,R.categories[E]={usd:Ur(L.usd),activeMs:L.activeMs};for(let[G,C]of Object.entries(L.models))R.models[G]=Ur((R.models[G]||0)+C);for(let[G,C]of Object.entries(L.providers))R.providers[G]=Ur((R.providers[G]||0)+C);o.has(E)||o.set(E,rU());let U=o.get(E);U.usd+=L.usd,U.activeMs+=L.activeMs,U.turns+=L.turns,U.toolCalls+=L.toolCalls;for(let[G,C]of Object.entries(L.models))U.models[G]=(U.models[G]||0)+C;for(let[G,C]of Object.entries(L.providers))U.providers[G]=(U.providers[G]||0)+C;let se=U.byTrack.get(y?.id)||{usd:0,activeMs:0};se.usd+=L.usd,se.activeMs+=L.activeMs,U.byTrack.set(y?.id,se)}(R.usd>0||R.activeMs>0||R.turns>0)&&(R.usd=Ur(R.usd),R.dominantCategory=sU(R.categories),s.push(R))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,p=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:f>0?Math.round((d==="usd"?y.usd:y.activeMs)/f*100):0,shareBasis:d,usd:Ur(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:iE(y.models),providers:iE(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((x,T)=>T[1].usd-x[1].usd||T[1].activeMs-x[1].activeMs).slice(0,2).map(([x,T])=>({trackId:x,name:p.get(x),usd:Ur(T.usd),activeMs:T.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:Ur(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var sU=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},iE=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ur(n)]));var na={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function cU(e){let t=String(e||"7d");return t in na?{key:t,ms:na[t]}:{key:"7d",ms:na["7d"]}}function lU(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=Pl(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 uU(e){if(!e?.cwd)return null;try{let n=(await aU.readFile(iU.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 lE(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}:cU(t),d=[];for(let f of e||[]){let p=await F(f),m=lU(f,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),d.push({track:f,events:m,brief:await uU(f)})}return{window:l,...cE(d,{windowMs:u,now:c})}}On();var Hr=36e5,pE=24*Hr,dU=400,fU=12,Mo=e=>Math.round(e*1e4)/1e4;function Mw({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 na?String(e||"7d"):"7d",i=na[a];return{key:a,since:i==null?null:r-i,until:r}}var pU=(e,t)=>e!=null&&t-e<=48*Hr?Hr:pE;function uE(e,t){if(t===Hr)return Math.floor(e/Hr)*Hr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function mU(e,t){if(t===Hr)return e+Hr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var dE=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),fE=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},hU=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Mo(n)]));async function mE(e,t={}){let n=t.now??Date.now(),r=Mw({...t,now:n}),o=r.since==null?pE:pU(r.since,r.until),s=E=>(r.since==null||E>=r.since)&&E<r.until,a=r.since!=null?r.until-r.since:null,i=E=>a!=null&&E>=r.since-a&&E<r.since,c={},l={},u=dE(),d=0,f=0,p=0,m=0,w=0,y=0,x=new Set,T=new Map,I=[],$=[],D=[],S=null;for(let E of e||[]){let U=(await F(E)).filter(C=>C.type==="cost.recorded");if(!U.length){E.archivedAt||D.push(E.name||E.id);continue}let se=[];for(let C of U){let Z=C.ts??0;s(Z)?se.push(C):i(Z)&&(y+=Ot(C).usd)}if(!se.length)continue;let G=wr(se,{boxLabels:lc(E)});d+=G.totalUsd,f+=G.turns,p+=G.repricedTurns||0,m+=G.asRecordedUsd||0,w+=G.unpricedTurns||0;for(let C of G.unpricedModels||[])x.add(C);fE(u,G.totalTokens);for(let[C,Z]of Object.entries(G.byModel))c[C]||={usd:0,events:0,tokens:dE()},c[C].usd=Mo(c[C].usd+Z.usd),c[C].events+=Z.events,fE(c[C].tokens,Z.tokens);for(let[C,Z]of Object.entries(G.byProvider))l[C]||={usd:0,events:0},l[C].usd=Mo(l[C].usd+Z.usd),l[C].events+=Z.events;for(let C of se){let{usd:Z}=Ot(C);if(!Z)continue;let Y=uE(C.ts??0,o),te=T.get(Y)??{t:Y,total:0,byProvider:{}};te.total+=Z;let we=C.provider||"unknown";te.byProvider[we]=(te.byProvider[we]||0)+Z,T.set(Y,te),(S==null||Y<S)&&(S=Y)}I.push({id:E.id,name:E.name||E.id,archived:!!E.archivedAt,usd:G.totalUsd,turns:G.turns,providers:Object.keys(G.byProvider)});for(let[C,Z]of Object.entries(G.byBox))$.push({trackId:E.id,trackName:E.name||E.id,boxId:C,...Z})}let A=r.since!=null?uE(r.since,o):S,R=[];for(let E=A,L=0;E!=null&&E<r.until&&L<dU;E=mU(E,o),L++){let U=T.get(E);R.push(U?{t:E,total:Mo(U.total),byProvider:hU(U.byProvider)}:{t:E,total:0,byProvider:{}})}return I.sort((E,L)=>L.usd-E.usd),$.sort((E,L)=>L.usd-E.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:ls(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:R,fleet:{totalUsd:Mo(d),asRecordedUsd:Mo(m),turns:f,repricedTurns:p,unpricedTurns:w,unpricedModels:[...x],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:I.length,tracksSilentLive:D},prior:a!=null?{totalUsd:Mo(y),since:r.since-a,until:r.since}:null,tracks:I.slice(0,50),boxes:$.slice(0,fU)}}su();var wU=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),gU=20,yU=40;async function hE(e,t={}){let n=t.now??Date.now(),r=Mw({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await F(l),d=0,f=lc(l);for(let p of u){if(!wU.has(p.type))continue;let m=p.type==="code.rework"?p.payload?.ts??p.ts:p.ts;p.type!=="rework.cause.answered"&&!o(m??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=jv(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:$r,decayEndDays:mm,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,gU),fixes:c.fixes.slice(0,yU)}}import bU from"node:fs";import wE from"node:fs/promises";import uc from"node:os";import kU from"node:path";Om();nt();var xU=1024*1024,Qu=e=>Math.round(e/xU);async function vU(e){try{let t=await wE.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Qu(r),diskTotalMb:Qu(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function SU(){try{return(await Ut()).filter(t=>t.isBostrat).length}catch{return null}}async function TU(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await wE.readdir(kU.join(Xi,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function gE({stateDir:e=Xi,now:t=Date.now}={}){let n=uc.totalmem(),[r,o,s,a]=await Promise.all([fu().catch(()=>null),vU(bU.existsSync(e)?e:uc.tmpdir()),SU(),TU()]),i=uc.cpus()?.length||null,c=uc.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?Qu(n-r):null,memTotalMb:Qu(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(uc.uptime())}}ue();Cf();Aa();On();ue();On();var AU=864e5;function EU(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=an(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 yE(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*AU,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await F(u);for(let f of d){if(!f.ts||f.ts<o)continue;let p=EU(f,{tolerance:n});if(!p)continue;a++;let m=p.speed?`${p.model} (${p.speed})`:p.model,w=s[m]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};w.rows++,w.asRecordedUsd+=p.asRecorded,w.expectedUsd+=p.expected,p.divergent&&(i++,w.divergent++,c.length<10&&c.push({track:u.name||u.id,...p}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}um();Cl();it();Cn();import ra from"node:fs/promises";import _U from"node:fs";import ot from"node:path";import IU from"node:os";import{execFile as $U}from"node:child_process";import{promisify as RU}from"node:util";var OU=RU($U),bE=process.env.BOSTRAT_STATE_DIR||ot.join(process.env.HOME||IU.tmpdir(),".bostrat"),ed=!1,Nw=null;async function Pw(e,t){let n=await ra.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ra.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ra.mkdir(ot.dirname(t),{recursive:!0}),await ra.copyFile(e,t),!0)}var CU=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 td(e,t,{skip:n=null}={}){let r=0,o=await ra.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(CU.has(s.name)||n?.has(s.name))continue;let a=ot.join(e,s.name);s.isDirectory()?r+=await td(a,ot.join(t,s.name)):s.isFile()&&(r+=await Pw(a,ot.join(t,s.name))?1:0)}return r}async function kE({stateDir:e=bE,dest:t=process.env.BOSTRAT_SYNC_DIR||`${bE}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ra.mkdir(t,{recursive:!0});let o=0;o+=await Pw(ot.join(e,"tracks.json"),ot.join(t,"tracks.json"))?1:0,o+=await Pw(ot.join(e,"events.jsonl"),ot.join(t,"events.jsonl"))?1:0,o+=await td(ot.join(e,"events"),ot.join(t,"events")),o+=await td(ot.join(e,"memory"),ot.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await td(ot.join(u.cwd,".bostrat"),ot.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>OU("git",["-C",t,...u]);_U.existsSync(ot.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 Md(t).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||ed))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,ed&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),ed=!1,Nw=null}catch(u){ed=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
639
|
-
`).slice(-3).join(" | ").slice(0,300);
|
|
640
|
-
`).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
|
|
641
|
-
`).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
|
|
642
|
-
`),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
|
|
643
|
-
`,{mode:384}),
|
|
672
|
+
_${o} archived_`:"")}async function mU(e,t){let n=await Uo();if(!n)return Ce("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 Ce("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(f=>f.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(Kr($A(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Ce(`Starting *${ke(u)}* on ${ke(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:fU(u,i)}}case"archive":{if(!a.length)return Ce("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Uw(await r.listTracks(),a.join(" "));return i?(Kr(lU(e,n,{track:i,responseUrl:t.response_url})),Ce(`Archiving *${ke(i.name)}*\u2026`)):Hw(c,a.join(" "))}case"handoff":{let i=r.knownAgents??cU,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Ce(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=Uw(await r.listTracks(),l);return u?(Kr(uU(e,n,{track:u,agent:c,responseUrl:t.response_url})),Ce(`Handing *${ke(u.name)}* to ${ke(c)}\u2026`)):Hw(f,l)}case"stop":{if(!a.length)return Ce("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Uw(await r.listTracks(),a.join(" "));return i?(Kr(dU(e,n,{track:i,responseUrl:t.response_url})),Ce(`Stopping *${ke(i.name)}*\u2026`)):Hw(c,a.join(" "))}case"list":return Ce(await pU(r));default:return Ce(iU)}}async function hU(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Gw(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 nn(),await qo(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ke(l||"question")}~
|
|
673
|
+
\u2713 *${ke(a)}*`}}]}).catch(()=>{})}}catch(c){await yt(n.response_url,Ce(`Couldn't answer: ${c?.message||c}`))}}async function wU(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(d=>d.type==="pr.offered"&&d.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===s))throw new Error("offer already answered");let f=Array.isArray(r.actions)?Ar.filter(d=>r.actions.includes(d)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(f?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:f}))return;f&&await i.sayToLead(c.id,Os(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:f?a:"dismiss",actions:f}})}catch(c){await yt(n.response_url,Ce(`Couldn't act on the offer: ${c?.message||c}`))}}async function gU(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 yt(n.response_url,Ce(`Couldn't act on the offer: ${i?.message||i}`))}}async function yU(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Uo();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 yt(t.response_url,{...Ce(`Starting *${ke(s)}*\u2026`),replace_original:!0}),Kr($A(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"?Kr(hU(e,r,t,o)):n.action_id==="bostrat-ship"?Kr(wU(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&Kr(gU(e,r,t,o))}async function bU(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 sU(t.channel,t.thread_ts);if(!n)return;let r=await Uo();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Gr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function kU(e){return async t=>{if(t.type==="slash_commands")return mU(e,t.payload);if(t.type==="interactive"){await yU(e,t.payload);return}if(t.type==="events_api"){await bU(e,t.payload?.event);return}}}var xU=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 vU(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${ke(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:hc(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 SU(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await qo(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ke(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Gr(e,t,r.question||"The lead needs an answer",vU(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await nn()}async function TU(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 nn(),await qo(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ke(s||"question")}~
|
|
674
|
+
\u2713 _answered_`}}]})}async function EU(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(f=>l[f]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await Gr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${ke(hc(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await nn()}async function AU(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await nn();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 qo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function _U(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 Gr(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${ke(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 nn()}async function IU(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await nn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await qo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function $U(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??RU)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await SA(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${ke(o.name)}*${a}${o.group?` (group \`${ke(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${ke(a.name)}*`).join(", ");await Gr(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${ke(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function RU(e,t){let n=await zn(e);if(!n)return null;let r=Zt(n,t);if(!r)return null;let o=await mc.readFile(Ft(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function OU(e,t){if(!xU.has(t.type))return;let n=await Gw(t.trackId);if(!n)return;let r=await Uo();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 nn(),await qw(r,n);return;case"turn.agent":{if(!o.text)return;let s=aU(o.text);if(!s)return;await Gr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:ke(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${rd(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return SU(r,n,t);case"prompt.answered":return TU(r,n,t);case"pr.offered":return EU(r,n,t);case"pr.offer.answered":return AU(r,n,t);case"pr.exists.offered":return _U(r,n,t);case"pr.exists.answered":return IU(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 Gr(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 nn(),await qw(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Gr(r,n,`Live preview ${o.name}: ${od(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${ke(o.name)}*: ${od(o.slug)}
|
|
675
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return $U(r,n,t,e.deps)}}function RA(e){let t={deps:e};ct.on("event",s=>{OU(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Uo()||(r&&clearInterval(r),r=null,n=TA({getToken:async()=>(await Uo())?.appToken,onEnvelope:kU(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}zt();de();cn();dm();Pn();import CU from"node:fs/promises";function MU(e){let t=new Map;for(let n of String(e||"").split(`
|
|
676
|
+
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function NU(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 PU=new Set(["Write","Edit","NotebookEdit"]),DU=220,BU=160;function jU(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(PU.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,DU)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=no(o.command.trim().replace(/\s+/g," ")).slice(0,BU));return r}function LU(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Vt(t.cwd,t.sessionId):t?.cwd?gf(t.cwd):null}async function OA(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=au(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:jU(e,o)});break;case"Stop":await FU(r,o,e,i);break;default:break}return a}async function FU(e,t,n,r){let o=[],s=LU(n,t);if(s)try{o=MU(await CU.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=NU(await F(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=`${f.model||"unknown"}\0${f.speed||""}`,p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let h of Object.keys(p.tokens))p.tokens[h]+=f.tokens[h];f.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=f.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=f.cacheWriteTtl.h1),p.messageIds.push(f.messageId),p.speed=f.speed||null,u.set(d,p)}for(let[f,d]of u){let p=f.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:dn(p,d.tokens,void 0,d.speed?{speed:d.speed}:void 0),tokens:d.tokens,...d.speed?{speed:d.speed}:{},cacheWriteTtl:d.cacheWriteTtl,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.messageIds.length}})}}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}Yo();lf();de();bl();hu();import{execFile as UU}from"node:child_process";import{promisify as HU}from"node:util";var WU=HU(UU),Vw=(e,t)=>WU("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4});function CA(e,t=Date.now()){return Vd(e,t)??0}function qU(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 MA(e,t,{now:n=Date.now()}={}){let r=CA(t,n),o=await F(e);return mu({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:qU(o,t.name,r)})}function KU(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=null;for(let c=n.length-1;c>=0;c--){let l=n[c];if(l.type==="turn.user"&&l.boxId===t.name&&l.actor==="user"){i=Nn(l.payload?.promptHead||l.payload?.text||"")?.category??null;break}}return{wallReason:a,fixerCategory:i}}async function GU(e,t){try{let{stdout:n}=await Vw(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 VU(e){try{let{stdout:t}=await Vw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function NA(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await F(e);if(a.some(p=>p.type==="code.committed"&&p.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:p}=await Vw(r,["show","-s","--format=%ct",n]),h=Number(p.trim())*1e3;Number.isFinite(h)&&h>0&&(i=h)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=CA(t,i),l=await GU(r,n),u=await $(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:await VU(r),...l,ts:i}}),f=null,d=!1;try{let p=await hS(r,n,{fixKey:Us({trackId:e.id,boxId:t.name,epoch:c}),window:Dr,context:{...KU(e,t,a,c),provider:t.agent??null}});if(p){f=await $(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:u.id,payload:p});let h=wS(p);if(h>=mS){let w=p.origins.filter(g=>g.class==="rework").slice(0,3);await $(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:f.id,payload:{sha:n,subject:p.subject,linesReworked:h,origins:w.map(g=>({key:g.key,provider:g.provider,model:g.model,lines:g.lines,ageDays:g.ageDays})),heuristic:p.heuristic,ts:i}}),d=!0}}}catch(p){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${p?.message||p}`)}return{recorded:!0,event:u,rework:f,offered:d}}async function PA(e,t,n){if(!Ii.includes(n))throw new Error(`bad cause "${n}" (${Ii.join("|")})`);let r=await F(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(r.some(s=>s.type==="rework.cause.answered"&&s.payload?.sha===t))throw new Error("already answered");return $(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,cause:n,level:"B",ts:Date.now()}})}de();de();vr();de();vr();Pn();function wc(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 DA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),BA=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},cd=e=>Math.round(e*1e4)/1e4;async function zw(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=DA(),f=0,d=[];for(let p of e||[]){let h=await F(p),w=xr(h,{boxLabels:wc(p)});if(w.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let g of w.unpricedModels||[])l.add(g);BA(u,w.totalTokens);for(let[g,k]of Object.entries(w.byModel))n[g]||={usd:0,events:0,tokens:DA()},n[g].usd=cd(n[g].usd+k.usd),n[g].events+=k.events,BA(n[g].tokens,k.tokens);for(let[g,k]of Object.entries(w.byProvider))r[g]||={usd:0,events:0},r[g].usd=cd(r[g].usd+k.usd),r[g].events+=k.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,h)=>h.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:bs(),fleet:{totalUsd:cd(o),asRecordedUsd:cd(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:t}}var Vr=e=>`$${e.toFixed(4)}`,ca=(e,t)=>String(e).padEnd(t);function jA(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];if(r.push(`fleet total ${Vr(t.totalUsd)} over ${t.turns} turn(s) \xB7 ${t.tracksWithCost} track(s) with cost, ${t.tracksSilentArchived??t.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),t.repricedTurns&&r.push(`${t.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),t.asRecordedUsd!=null&&Math.abs(t.asRecordedUsd-t.totalUsd)>=.005&&r.push(`as-recorded total ${Vr(t.asRecordedUsd)} \xB7 current-rates total ${Vr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Vr(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${ca(a,26)} ${ca(Vr(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${ca(a.name||a.id,30)} ${Vr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ca(c.label,40)} ${ca(Vr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${ca(Vr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
677
|
+
`)}de();qp();import ZU from"node:fs/promises";import QU from"node:path";bl();vr();var ld=e=>e.payload?.ts??e.ts,zU=12e4,zr=e=>Math.round(e*1e4)/1e4,YU=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function JU(e,{since:t}){let{track:n,events:r,brief:o}=e,s=S=>ld(S)>=t,a=Nn([ni(n?.name),ni(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let A=Nn(S.payload.task);A&&i.set(S.boxId,A)}let c=new Map,l=S=>{let A=S||"unattributed";return c.has(A)||c.set(A,{segments:[u(null)],lastTs:null,provider:null}),c.get(A)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((S,A)=>ld(S)-ld(A)),d=0,p=0,h=0,w={turns:0};for(let S of f){let A=l(S.boxId),R=A.segments[A.segments.length-1],B=ld(S);switch(A.lastTs!=null&&B>A.lastTs&&s(S)&&(R.activeMs+=Math.min(B-A.lastTs,zU)),A.lastTs=B,S.provider&&(A.provider=S.provider),S.type){case"turn.user":{let T=S.payload?.text;if(S.source==="hook"||!String(T||"").trim())break;let _=u(Nn(T));A.segments.push(_),s(S)&&(_.turns+=1);break}case"peer.message":{let T=S.payload?.text;if(!String(T||"").trim())break;let _=u(Nn(T));A.segments.push(_),s(S)&&(_.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let T=S.payload?.tool;T&&(R.tools[T]=(R.tools[T]||0)+1),s(S)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let{usd:T,tokens:_,model:O,unpriced:I}=Bt(S);I&&(w.turns+=1),R.usd+=T,_&&(R.tokens.input+=_.input,R.tokens.output+=_.output,R.tokens.cacheRead+=_.cacheRead,R.tokens.cacheWrite+=_.cacheWrite),R.models[O]=(R.models[O]||0)+T,S.provider&&(R.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let g=new Map,k=!1;for(let[S,A]of c)for(let R of A.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let T=R.textCat?.category??i.get(S)?.category??Hb(R.tools)?.category??a?.category??"uncategorized";g.has(T)||g.set(T,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let _=g.get(T);_.usd+=R.usd,_.activeMs+=R.activeMs,_.turns+=R.turns,_.toolCalls+=R.toolCalls;for(let[I,U]of Object.entries(R.models))_.models[I]=(_.models[I]||0)+U;let O=R.provider||A.provider;O&&(_.providers[O]=(_.providers[O]||0)+R.usd),O==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:g,handoffs:d,merged:p,failed:h,sawCursorActivity:k,unpricedTurns:w.turns}}function FA(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let w of e||[]){let{track:g,events:k}=w;if(!k?.length){a+=1;continue}let{perCategory:S,handoffs:A,merged:R,failed:B,sawCursorActivity:T,unpricedTurns:_}=JU(w,{since:r});T&&(i=!0),c+=_;let O={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:A,subtasksMerged:R,subtasksFailed:B};for(let[I,U]of S){O.usd+=U.usd,O.activeMs+=U.activeMs,O.turns+=U.turns,O.categories[I]={usd:zr(U.usd),activeMs:U.activeMs};for(let[J,C]of Object.entries(U.models))O.models[J]=zr((O.models[J]||0)+C);for(let[J,C]of Object.entries(U.providers))O.providers[J]=zr((O.providers[J]||0)+C);o.has(I)||o.set(I,YU());let H=o.get(I);H.usd+=U.usd,H.activeMs+=U.activeMs,H.turns+=U.turns,H.toolCalls+=U.toolCalls;for(let[J,C]of Object.entries(U.models))H.models[J]=(H.models[J]||0)+C;for(let[J,C]of Object.entries(U.providers))H.providers[J]=(H.providers[J]||0)+C;let ae=H.byTrack.get(g?.id)||{usd:0,activeMs:0};ae.usd+=U.usd,ae.activeMs+=U.activeMs,H.byTrack.set(g?.id,ae)}(O.usd>0||O.activeMs>0||O.turns>0)&&(O.usd=zr(O.usd),O.dominantCategory=XU(O.categories),s.push(O))}let l=[...o.values()].reduce((w,g)=>w+g.usd,0),u=[...o.values()].reduce((w,g)=>w+g.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),h=[...o.entries()].map(([w,g])=>({category:w,sharePct:d>0?Math.round((f==="usd"?g.usd:g.activeMs)/d*100):0,shareBasis:f,usd:zr(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:LA(g.models),providers:LA(g.providers),topWorkstreams:[...g.byTrack.entries()].sort((k,S)=>S[1].usd-k[1].usd||S[1].activeMs-k[1].activeMs).slice(0,2).map(([k,S])=>({trackId:k,name:p.get(k),usd:zr(S.usd),activeMs:S.activeMs}))})).sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs);return s.sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:zr(l),activeMs:u,turns:[...o.values()].reduce((w,g)=>w+g.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var XU=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},LA=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,zr(n)]));var la={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function e4(e){let t=String(e||"7d");return t in la?{key:t,ms:la[t]}:{key:"7d",ms:la["7d"]}}function t4(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=Hl(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 n4(e){if(!e?.cwd)return null;try{let n=(await ZU.readFile(QU.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 UA(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}:e4(t),f=[];for(let d of e||[]){let p=await F(d),h=t4(d,p);i&&c<o&&(h=h.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:h,brief:await n4(d)})}return{window:l,...FA(f,{windowMs:u,now:c})}}Pn();var Yr=36e5,KA=24*Yr,r4=400,o4=12,Ko=e=>Math.round(e*1e4)/1e4;function Yw({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in la?String(e||"7d"):"7d",i=la[a];return{key:a,since:i==null?null:r-i,until:r}}var s4=(e,t)=>e!=null&&t-e<=48*Yr?Yr:KA;function HA(e,t){if(t===Yr)return Math.floor(e/Yr)*Yr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function a4(e,t){if(t===Yr)return e+Yr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var WA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),qA=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},i4=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ko(n)]));async function GA(e,t={}){let n=t.now??Date.now(),r=Yw({...t,now:n}),o=r.since==null?KA:s4(r.since,r.until),s=I=>(r.since==null||I>=r.since)&&I<r.until,a=r.since!=null?r.until-r.since:null,i=I=>a!=null&&I>=r.since-a&&I<r.since,c={},l={},u=WA(),f=0,d=0,p=0,h=0,w=0,g=0,k=new Set,S=new Map,A=[],R=[],B=[],T=null;for(let I of e||[]){let H=(await F(I)).filter(C=>C.type==="cost.recorded");if(!H.length){I.archivedAt||B.push(I.name||I.id);continue}let ae=[];for(let C of H){let Q=C.ts??0;s(Q)?ae.push(C):i(Q)&&(g+=Bt(C).usd)}if(!ae.length)continue;let J=xr(ae,{boxLabels:wc(I)});f+=J.totalUsd,d+=J.turns,p+=J.repricedTurns||0,h+=J.asRecordedUsd||0,w+=J.unpricedTurns||0;for(let C of J.unpricedModels||[])k.add(C);qA(u,J.totalTokens);for(let[C,Q]of Object.entries(J.byModel))c[C]||={usd:0,events:0,tokens:WA()},c[C].usd=Ko(c[C].usd+Q.usd),c[C].events+=Q.events,qA(c[C].tokens,Q.tokens);for(let[C,Q]of Object.entries(J.byProvider))l[C]||={usd:0,events:0},l[C].usd=Ko(l[C].usd+Q.usd),l[C].events+=Q.events;for(let C of ae){let{usd:Q}=Bt(C);if(!Q)continue;let ve=HA(C.ts??0,o),W=S.get(ve)??{t:ve,total:0,byProvider:{}};W.total+=Q;let be=C.provider||"unknown";W.byProvider[be]=(W.byProvider[be]||0)+Q,S.set(ve,W),(T==null||ve<T)&&(T=ve)}A.push({id:I.id,name:I.name||I.id,archived:!!I.archivedAt,usd:J.totalUsd,turns:J.turns,providers:Object.keys(J.byProvider)});for(let[C,Q]of Object.entries(J.byBox))R.push({trackId:I.id,trackName:I.name||I.id,boxId:C,...Q})}let _=r.since!=null?HA(r.since,o):T,O=[];for(let I=_,U=0;I!=null&&I<r.until&&U<r4;I=a4(I,o),U++){let H=S.get(I);O.push(H?{t:I,total:Ko(H.total),byProvider:i4(H.byProvider)}:{t:I,total:0,byProvider:{}})}return A.sort((I,U)=>U.usd-I.usd),R.sort((I,U)=>U.usd-I.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:bs(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:O,fleet:{totalUsd:Ko(f),asRecordedUsd:Ko(h),turns:d,repricedTurns:p,unpricedTurns:w,unpricedModels:[...k],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:A.length,tracksSilentLive:B},prior:a!=null?{totalUsd:Ko(g),since:r.since-a,until:r.since}:null,tracks:A.slice(0,50),boxes:R.slice(0,o4)}}hu();var c4=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),l4=20,u4=40;async function VA(e,t={}){let n=t.now??Date.now(),r=Yw({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await F(l),f=0,d=wc(l);for(let p of u){if(!c4.has(p.type))continue;let h=p.type==="code.rework"?p.payload?.ts??p.ts:p.ts;p.type!=="rework.cause.answered"&&!o(h??0)||(p.type==="code.committed"&&f++,s.push(p.trackId?p:{...p,trackId:l.id}))}f&&i++;for(let p of l.boxes||[])a[`${l.id}/${p.name}`]={trackName:l.name||l.id,label:d[p.name]||p.name}}let c=gS(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:Dr,decayEndDays:Rm,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,l4),fixes:c.fixes.slice(0,u4)}}import d4 from"node:fs";import zA from"node:fs/promises";import gc from"node:os";import f4 from"node:path";Vm();lt();var p4=1024*1024,ud=e=>Math.round(e/p4);async function m4(e){try{let t=await zA.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:ud(r),diskTotalMb:ud(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function h4(){try{return(await Kt()).filter(t=>t.isBostrat).length}catch{return null}}async function w4(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await zA.readdir(f4.join(oc,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function YA({stateDir:e=oc,now:t=Date.now}={}){let n=gc.totalmem(),[r,o,s,a]=await Promise.all([vu().catch(()=>null),m4(d4.existsSync(e)?e:gc.tmpdir()),h4(),w4()]),i=gc.cpus()?.length||null,c=gc.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?ud(n-r):null,memTotalMb:ud(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(gc.uptime())}}de();Hf();Ra();Pn();de();Pn();var g4=864e5;function y4(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=dn(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 JA(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*g4,s={},a=0,i=0,c=[];for(let u of e||[]){let f=await F(u);for(let d of f){if(!d.ts||d.ts<o)continue;let p=y4(d,{tolerance:n});if(!p)continue;a++;let h=p.speed?`${p.model} (${p.speed})`:p.model,w=s[h]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};w.rows++,w.asRecordedUsd+=p.asRecorded,w.expectedUsd+=p.expected,p.divergent&&(i++,w.divergent++,c.length<10&&c.push({track:u.name||u.id,...p}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}Am();Ll();it();Dn();import ua from"node:fs/promises";import b4 from"node:fs";import ft from"node:path";import k4 from"node:os";import{execFile as x4}from"node:child_process";import{promisify as v4}from"node:util";var S4=v4(x4),XA=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||k4.tmpdir(),".bostrat"),dd=!1,Jw=null;async function Xw(e,t){let n=await ua.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ua.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ua.mkdir(ft.dirname(t),{recursive:!0}),await ua.copyFile(e,t),!0)}var T4=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 fd(e,t,{skip:n=null}={}){let r=0,o=await ua.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(T4.has(s.name)||n?.has(s.name))continue;let a=ft.join(e,s.name);s.isDirectory()?r+=await fd(a,ft.join(t,s.name)):s.isFile()&&(r+=await Xw(a,ft.join(t,s.name))?1:0)}return r}async function ZA({stateDir:e=XA,dest:t=process.env.BOSTRAT_SYNC_DIR||`${XA}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ua.mkdir(t,{recursive:!0});let o=0;o+=await Xw(ft.join(e,"tracks.json"),ft.join(t,"tracks.json"))?1:0,o+=await Xw(ft.join(e,"events.jsonl"),ft.join(t,"events.jsonl"))?1:0,o+=await fd(ft.join(e,"events"),ft.join(t,"events")),o+=await fd(ft.join(e,"memory"),ft.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await fd(ft.join(u.cwd,".bostrat"),ft.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>S4("git",["-C",t,...u]);b4.existsSync(ft.join(t,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Wd(t).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||dd))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,dd&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),dd=!1,Jw=null}catch(u){dd=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
678
|
+
`).slice(-3).join(" | ").slice(0,300);f!==Jw&&(Jw=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}fh();zt();io();lt();Sa();import{execFile as E4}from"node:child_process";import{promisify as A4}from"node:util";import{randomUUID as _4}from"node:crypto";import I4 from"node:fs";import $4 from"node:os";import R4 from"node:path";var Xr=A4(E4),pd=e=>new Promise(t=>setTimeout(t,e)),Zr=ff,Zw=process.env.BOSTRAT_USAGE_PROBE_CWD||R4.join($4.homedir(),".bostrat","usage-probe"),O4=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,C4=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,M4=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,QA=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Jr=null,yc=null,bc=null;async function N4(){try{return await Xr(K,G("has-session","-t",Zr)),!0}catch{return!1}}function P4(){Jr&&clearTimeout(Jr),Jr=setTimeout(()=>D4().catch(()=>{}),O4),Jr.unref&&Jr.unref()}async function D4(){Jr&&(clearTimeout(Jr),Jr=null);try{await Xr(K,G("kill-session","-t",Zr))}catch{}}async function B4(){let e=Date.now()+M4;for(;;){let t=await Qw().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await pd(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await pd(500)}}async function j4(){if(await N4())return;if(!await fr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");I4.mkdirSync(Zw,{recursive:!0}),await or(Zw);let e=_4();await Xr(K,G("new-session","-d","-s",Zr,"-c",Zw,"-x","200","-y","60",...La()));let t=`${df} --session-id ${e}`;await Xr(K,G("send-keys","-t",Zr,t,"Enter")),await B4()}async function Qw(){let{stdout:e}=await Xr(K,G("capture-pane","-t",Zr,"-p"));return e}async function L4(){await j4(),await Xr(K,G("send-keys","-t",Zr,"-l","/usage")),await Xr(K,G("send-keys","-t",Zr,"Enter"));let e=Date.now()+C4,t="";for(;await pd(400),t=await Qw(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await pd(900),t=await Qw(),await Xr(K,G("send-keys","-t",Zr,"Escape")).catch(()=>{}),P4(),t}function F4(e){let t=String(e||"").split(`
|
|
679
|
+
`).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 U4(e){let t=String(e||"").split(`
|
|
680
|
+
`).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 e_({fresh:e=!1}={}){return!e&&yc&&Date.now()-yc.at<QA?yc.data:bc||(bc=(async()=>{try{let t=await L4(),n=F4(t),r={...n,contributors:U4(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return yc={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 yc={at:Date.now()-(QA-3e3),data:n},n}finally{bc=null}})(),bc)}var H4=262144;function W4(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 q4=new Set([300]),K4=new Set([10080]);function G4(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:q4.has(t)?"session":K4.has(t)?"week":"other"}function V4(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 z4(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?W4((o-n)/1e3):null,resetsAt:o}}var kc=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function Y4(e,t=Date.now()){let n=String(e||"").split(`
|
|
681
|
+
`),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 kc(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=kc(r);[c.primary,c.secondary].forEach((p,h)=>{let w=z4(p,l,t);if(!w)return;let g=G4(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:V4(p.window_minutes),...w})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let d=c.credits;return d&&(d.has_credits||d.unlimited)&&(u.credits={balance:String(d.balance??""),unlimited:!!d.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...kc(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return kc(r)}return kc(r)}async function eg(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await so(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 St(n.file,H4),o=Y4(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 t_(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 md({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 eg(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await eg(null))}return n.has("cursor")&&r.push(await md({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function md(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?eg(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await e_({fresh:t})}}ar();it();it();function J4(e=process.env){return!!(_e(e)&&e.BOSTRAT_PLANE_TOKEN)}var X4=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),hd=(e,t)=>Object.assign(new Error(e),{status:t});async function n_(e,t){if(!J4(t))throw hd("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${_e(t)}${e}`,{headers:X4(t)})}catch(o){throw hd(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw hd(r?.error||`plane replied ${n.status}`,n.status);return r}async function r_(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await n_(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function o_(e,{env:t=process.env}={}){if(!e)throw hd("message id required",400);return(await n_(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}wi();Ql();function tg(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 Z4(e){let[t,n]=e.split("/"),r=tg(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 s_(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(Z4).filter(Boolean)}function Q4(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function a_(e){let t=s_(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:Q4,allowed(r){if(n)return!0;let o=tg((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function i_(e,t){let n=s_(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=tg(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import c_ from"node:crypto";import wd from"node:fs";import e2 from"node:path";function l_(e=process.env,{stateDir:t=oc,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=e2.join(t,"api-token");try{let a=wd.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=c_.randomBytes(32).toString("base64url");try{wd.mkdirSync(t,{recursive:!0}),wd.writeFileSync(o,s+`
|
|
682
|
+
`,{mode:384}),wd.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function xc(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 u_(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:c_.timingSafeEqual(n,r)}function d_(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 t2=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],n2=e=>t2.some(t=>t.test(e));function f_({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=d_(o);return u_(c,e)?a():n2(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||xc(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 p_(e,t,{enforce:n=!0}={}){return!n||u_(d_(e),t)?!0:xc(e)}cn();ku();ku();import r2 from"node:http";import o2 from"node:net";function gd(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 yd(e){let t=gd(e.headers.host);return t?{slug:t,view:Hm(t)}:null}var s2=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),a2=e=>{let t=s2(e.name);return`<!doctype html><meta charset="utf-8">
|
|
644
683
|
<meta http-equiv="refresh" content="3">
|
|
645
684
|
<title>${t} \u2014 starting</title>
|
|
646
685
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
647
686
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
648
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
687
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function m_(e,t,n){let r=yd(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(a2(o));if(o.state!=="live"&&o.state!=="unhealthy")return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...e.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":e.headers.host,"x-forwarded-proto":e.headers["x-forwarded-proto"]||"http"};delete a.authorization,delete a.cookie;let i=r2.request({host:"127.0.0.1",port:o.port,method:e.method,path:e.url,headers:a},c=>{t.writeHead(c.statusCode||502,c.headers),c.pipe(t)});i.on("error",()=>{t.headersSent?t.destroy():(t.statusCode=502,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),e.pipe(i),e.on("aborted",()=>i.destroy())}function h_(e,t,n){let r=yd(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
|
|
649
688
|
\r
|
|
650
689
|
bostrat: no live view "${s}"\r
|
|
651
|
-
`),t.destroy(),!0;let a=
|
|
690
|
+
`),t.destroy(),!0;let a=o2.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${e.url} HTTP/1.1`];for(let[l,u]of Object.entries(e.headers))l!=="host"&&(l==="authorization"||l==="cookie"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
652
691
|
`)+`\r
|
|
653
692
|
\r
|
|
654
|
-
`),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 jE(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}.${h4(n.host)}/`}function h4(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 ad(t)&&Em(n[0])?n.slice(1).join("."):t}js();Lm();gu();rm();ue();import LE from"node:os";import{randomUUID as Uw}from"node:crypto";var w4=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||LE.hostname(),nodeName:e.BOSTRAT_NODE_NAME||LE.hostname()});var FE=32768,UE=120,HE=1e3,WE=new Set(["claude","codex","cursor"]),g4=new Set(["off","transition","nag"]),y4=[[0,59],[0,23],[1,31],[1,12],[0,6]];function b4(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=y4[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 Qt=e=>String(e??"").trim(),No={name(e){let t=Qt(e);if(!t)throw new Error("a loop needs a name");if(t.length>UE)throw new Error(`name is too long (${t.length}); cap is ${UE}`);return t},schedule(e){let t=Qt(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!b4(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Qt(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>FE)throw new Error(`prompt is too long (${t.length} chars); cap is ${FE}`);return t},expectation(e){let t=Qt(e)||null;if(t&&t.length>HE)throw new Error(`expectation is too long (${t.length}); cap is ${HE}`);return t},notify(e){let t=Qt(e)||"off";if(!g4.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Qt(typeof t=="string"?t:t?.provider)||"claude";if(!WE.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...WE].join(", ")}`);let r=typeof t=="object"&&t&&Qt(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 k4(e,t){let n=e&&typeof e=="object"?e:{},r=No.name(n.name),o=No.schedule(n.schedule),s=No.prompt(n.prompt),a=No.expectation(n.expectation),i=No.notify(n.notify),c=No.assignment(n.assignment),l=Qt(t?.workspace?.repo)||Qt(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}=w4();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function KE(e,t){let n=k4(t,e),r=Uw();return await O({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 GE(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await F({id:e.id,cwd:e.cwd}),a=s.find(c=>c.type==="loop.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="loop.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await O({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 O({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 O({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 Fw=["name","schedule","prompt","expectation","notify","assignment","enabled"],x4=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function VE(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 zE(e,t){let n=Qt(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 v4(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=>!Fw.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: ${Fw.join(", ")}`)}let o={};for(let a of Fw){if(!(a in n))continue;let i=No[a](n[a]);(a==="assignment"?x4(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 JE(e,t,n){let r=VE(t),{patch:o,changed:s}=v4(n,r),a=Uw(),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 O({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var qE=600;async function YE(e,t,n){let r=Qt(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>qE)throw new Error(`reason is too long (${r.length}); cap is ${qE}`);let o=VE(t),s=Uw(),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 O({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var XE=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function ZE(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 XE(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await O(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 O(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 QE(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 XE(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await O(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 O(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}}sr();ue();Pn();gu();var e_=10,t_=200,S4=120,T4=24,r_=/^\d{4}-\d{2}-\d{2}$/;function Hw(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var A4=(e=Date.now())=>new Date(e).toISOString().slice(0,10),st=(e,t="i")=>new RegExp(e,t),E4=[{re:st("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:st("\\bSPC\\b",""),reason:"business (SPC)"},{re:st("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:st("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:st("\\bReplicas\\b",""),reason:"competitor research"},{re:st("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:st("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:st("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],_4=[st("\\bSPC\\b",""),st("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),st("competitor|cloudcli|\\bkiro\\b"),st("\\bReplicas\\b",""),st("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),st("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),st("(^|[^\\w&])#\\d+\\b"),st("\\btrack/[\\w.-]+")];function I4({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of E4)if(o.test(n)||o.test(r))return s;return null}function n_(e){let t=String(e||"");for(let n of _4){let r=n.exec(t);if(r)return r[0].trim()}return null}function o_(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 $4=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function R4(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Ri(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(`
|
|
655
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,S4)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${T4}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=$4.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=n_(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>t_)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${t_}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=n_(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>e_)throw new Error(`${r.length} bullets \u2014 over the ${e_}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function O4(e,{repos:t=Hw()}={}){let n=new Set(t);return(await po()).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:I4({title:r.title,headRef:r.headRef})}))}async function s_(e,t){let n=await O4(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function a_(e,{md:t,day:n=null}){let r=R4(t),o=n==null||n===""?A4():String(n);if(!r_.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await O({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 i_(e,{day:t}){let n=String(t||"");if(!r_.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await O({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}}$s();Pn();Cs();ue();$s();it();_r();Pn();di();Gl();gi();Cn();import{execFile as C4}from"node:child_process";import{promisify as M4}from"node:util";var qw=M4(C4),c_=3e4,N4=30*6e4,P4=c_,D4=10*6e4,B4=10080*6e4,j4=60*6e4,L4=8,F4=1440*6e4,U4=3,l_="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Ww=60*6e4,H4=15*6e4,W4=9e4,q4=1440*6e4,cd=new Map,ld=new Map,K4=/could not resolve|not found|no pull requests?/i,G4=e=>K4.test(String(e?.message||e||""));function V4(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Ww)}var u_={fast:P4,slow:D4,decay:j4};function d_(e,t){return e.armedAt&&t-e.armedAt<N4||V4(e,t)||f_(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>B4?"decay":"slow"}function z4(e,t){return u_[d_(e,t)]}function wc(e,t,n,r){let o=e.lanes?.get(re(t.repo,t.number))||{};return Kl({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function J4(e,t){Bn("watch");try{let{stdout:n}=await qw("gh",["pr","view",String(t),"--repo",e,"--json",l_],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await tn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Tr(n)&&Ar(n),n}}async function Y4(e){Bn("watch");try{let{stdout:t}=await qw("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",l_],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await tn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Tr(t)&&Ar(t),t}}async function X4(){let e=new Map;for(let t of await ie().catch(()=>[]))e.set(t.id,t);return e}async function Z4(e,t){await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function Q4(e,t){let n=await ho(e);return n?_s(n,t):null}async function e2(e,t,n){let r=await ho(e);if(!r)throw new Error(`no local checkout for ${e}`);return Is(r,t,{method:n})}async function t2(e,t){let n=await ho(e);if(!n)throw new Error(`no local checkout for ${e}`);return mi(n,t)}async function n2(e,t){await O({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 oa(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 r2=10*6e4;async function o2(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>F4){await oa(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 oa(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await oa(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<r2)return;await oa(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 oa(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 oa(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await p_(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function sa(e,t,n){await O({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 s2(e,t){try{return await qw("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function hc(e,t,n,r=()=>!0){return(await F(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var aa=(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 f_(e,t,{grace:n=W4}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=q4}async function a2(e,t,n,r){let o=r.now??Date.now();if(!f_(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||qe(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??wo)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await F(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??Nn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??Nn)(e.repo,e.number).catch(()=>null)?(await sa(s,"opened",{...aa(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 i2(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 Bl(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Ll(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<H4;await wx(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await hx(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await hc(a,"pushed",e.number,d=>d.payload?.sha===s)&&await sa(a,"pushed",{...aa(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Ww)await jl(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 jl(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=wc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await hc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await sa(a,"ci-fixed",{...aa(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await jl(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Ww)await Fl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Fl(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){wc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await hc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await sa(a,"reconciled",{...aa(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Fl(e.repo,e.number).catch(()=>{})}}}}async function c2(e,t,n,r){let o=wc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await hc(s,"closed",e.number)&&await sa(s,"closed",{...aa(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 l2(e,t,n){wc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await hc(r,"lost",e.number)&&await sa(r,"lost",aa(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function p_(e,t,n,r,o={}){let s=wc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await F(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function u2(e={}){let{now:t=Date.now(),auth:n=Wp,view:r=J4,list:o=Y4,links:s=bx,trackMap:a=X4,emitMerged:i=Z4,mark:c=vr,archiveIfArmed:l=Ln,notifyIfArmed:u=Fn,notifyIfResolved:d=Iu,batchThreshold:f=U4,detail:p=Q4,merge:m=e2,updateBranch:w=t2,lookup:y=Qe,disarm:x=ui,emitDisarmed:T=n2,localHasCommit:I=s2,claim:$=Nn,midTurn:D=wo}=e;if(!(await n().catch(()=>null))?.authenticated||Wl()>0)return 0;let A=await s().catch(()=>[]),R=Wx(A),E=A.filter(Y=>t-(cd.get(re(Y.repo,Y.number))||0)>=z4(Y,t));if(!E.length)return 0;let L=Date.now(),U=new Map,se=new Map;for(let Y of[...E].sort(hi)){let te=d_(Y,t);U.set(re(Y.repo,Y.number),{lane:te,intervalMs:u_[te]}),cd.set(re(Y.repo,Y.number),t),se.has(Y.repo)||se.set(Y.repo,[]),se.get(Y.repo).push(Y)}let G=await a().catch(()=>new Map),C={now:t,lanes:U,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:m,updateBranch:w,lookup:y,disarm:x,emitDisarmed:T,localHasCommit:I,claim:$,midTurn:D};for(let[Y,te]of se){let we,ce=new Set;if(te.length>f)we=await o(Y).catch(()=>null);else{we=new Map;for(let V of te)try{let ge=await r(Y,V.number);ge?.number!=null&&we.set(Number(ge.number),ge)}catch(ge){G4(ge)&&ce.add(re(V.repo,V.number))}}if(we)for(let V of te){let ge=re(V.repo,V.number),B=we.get(Number(V.number));if(!B){if(ce.has(ge)){let ye=(ld.get(ge)||0)+1;ld.set(ge,ye),ye>=L4&&(await l2(V,G,C),ld.delete(ge),cd.delete(ge))}continue}if(ld.delete(ge),B.state==="MERGED"||B.state==="CLOSED")B.state==="MERGED"?await p_(V,B,G,C):await c2(V,B,G,C),cd.delete(ge);else{await a2(V,B,G,C).catch(()=>{}),await i2(V,B,G,C).catch(()=>{});let ye=!V.trackId||R.get(V.trackId)===ge;V.autoMerge&&ye&&await o2(V,G,C).catch(()=>{})}}}let Z={};for(let{lane:Y}of U.values())Z[Y]=(Z[Y]||0)+1;return console.log(`[pr-watch] probed ${E.length} link(s) (${Object.entries(Z).map(([Y,te])=>`${Y} ${te}`).join(", ")}) in ${Date.now()-L}ms`),E.length}function m_(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await u2()}catch{}finally{e=!1}}},c_)}ue();Pn();tr();_r();Cn();function h_(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function d2(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var f2=new Set(["merged","closed","lost"]);function p2(e,t){if(!e?.blocked)return null;let n=h_(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 m2(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function h2(e,t,n,r,o){return(await F(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function w2(e,t,n){await O({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={}){let{now:n=Date.now(),slugOf:r=xe,lookup:o=Qe,note:s=gx,tracks:a=async()=>(await ie()).filter(d=>!qe(d)),asked:i=h2,emitChange:c=w2}=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(!d2(p))continue;let m=await o(f,p.number).catch(()=>null);if(!m?.trackId||f2.has(m.state))continue;let{prev:w}=await s(f,p.number,{blocked:h_(p),sha:p.headSha??null,at:n});if(!w)continue;let y=p2(w,p);if(!y||m2(m,y.change))continue;u??=new Map((await a()).map($=>[$.id,$]));let x=u.get(m.trackId);if(!x)continue;let T=p.headSha??null;if(await i(x,y.change,p.number,T,w.at||0))continue;let I={number:Number(p.number),url:p.url||m.url,title:p.title||m.title,branch:p.headRefName||m.headRef,repo:f,sha:T,delegated:!1,reason:y.reason};y.change==="ci-fixed"?I.passed=p.checks.passed:I.base=p.baseRefName||null,await c(x,y.change,I),l.push({change:y.change,number:p.number,reason:y.reason})}catch{}}return l}nt();ue();_r();Cn();import{randomUUID as g2}from"node:crypto";var y2=2e4,g_=3,ia=new Map,Kw=new Set;async function y_(e,t,n){let r=await n(e).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===t?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function b2(e={}){let{sessions:t=null,tracks:n=null,append:r=O,read:o=F,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Ut(),i=n??await ie()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(qe(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=(ia.get(d.name)||0)+1;ia.set(d.name,p),p===g_&&(Kw.add(d.name),await y_(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:g2(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(ia.get(d.name)||0)>=g_;if(ia.delete(d.name),p||!Kw.has(d.name)){Kw.add(d.name);let m=await y_(u,d.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ia.keys()])l.has(u)||ia.delete(u)}var gc=null,Gw=!1;function b_(){return gc?()=>{}:(gc=setInterval(()=>{Gw||(Gw=!0,b2().catch(()=>{}).finally(()=>{Gw=!1}))},y2),gc.unref?.(),()=>{clearInterval(gc),gc=null})}Ss();$m();ue();$s();Cs();Ss();import{randomUUID as k2}from"node:crypto";async function x2(){let e=await Promise.resolve().then(()=>(_r(),KT));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Vw={listRepoPrs:cn,readEvents:F,appendEvent:O,sayDeps:x2,currentBranch:Rs};async function v2(e,t=Vw,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Rs)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function zw(e,t,{actions:n,notify:r=!1}={},o=Vw){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 v2(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=k2();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 Jw(e,{offerId:t,choice:n}={},r=Vw){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=xs.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,vs(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,sx(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 Zn from"node:fs/promises";import ca from"node:path";import E_ from"node:os";import k_ from"node:fs/promises";import ud from"node:path";import{execFile as S2}from"node:child_process";import{promisify as T2}from"node:util";import{fileURLToPath as A2}from"node:url";var E2=T2(S2),v_=ud.dirname(A2(import.meta.url)),_2=ud.resolve(v_,"..","..","client","dist"),I2="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$2=new Map([...I2].map((e,t)=>[e,t]));function R2(e){let t=[],n=0,r=0;for(let o of e){let s=$2.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 O2(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=R2(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 C2(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 x_=new Map;async function M2(e){let t;try{t=await k_.stat(e)}catch{return null}let n=x_.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await k_.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:O2(r.mappings||"")};return x_.set(e,o),o}catch{return null}}var Yw=null;function N2(){return Yw||(Yw=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):E2("git",["rev-parse","--short","HEAD"],{cwd:ud.resolve(v_,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Yw}var P2=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function D2(e,{distDir:t=_2}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(P2)){let[a,i,c,l]=s,u=await M2(ud.join(t,"assets",`${i}.map`)),d=u?C2(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 S_(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await N2();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await D2(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var B2=()=>process.env.BOSTRAT_STATE_DIR||ca.join(process.env.HOME||E_.tmpdir(),".bostrat"),Gr=()=>ca.join(B2(),"telemetry"),__=()=>ca.join(Gr(),".relay-offsets.json"),j2=50,L2=14,F2=3e4,T_=500,It=(e,t)=>typeof e=="string"?e.slice(0,t):"";function U2(e,t){if(!e||typeof e!="object")return null;let n=It(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:It(e.anonId,64),sessionId:It(e.sessionId,64),userId:It(e.userId,64),release:It(e.release,64),appVersion:It(e.appVersion,32),route:It(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:It(e.error.kind,256),message:It(e.error.message,8192),stack:It(e.error.stack,32768),fingerprint:It(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:It(s?.n,64),r:It(s?.r,128)}))),o}var I_=e=>new Date(e).toISOString().slice(0,10),A_=0;async function $_(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,j2):[]).map(s=>U2(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await S_(s);await Zn.mkdir(Gr(),{recursive:!0});let o=ca.join(Gr(),`${I_(t)}.ndjson`);return await Zn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
656
|
-
`).join(""),"utf8"),t-
|
|
657
|
-
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=T_){let p=d.slice(f,f+T_).map(w=>W2(w)),m=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await C_(r)}return{sent:o,disabled:!1}}var Xw=null,Zw=!1;function M_(){Xw||(Xw=setInterval(async()=>{if(!Zw){Zw=!0;try{await q2()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Zw=!1}}},F2),Xw.unref?.())}import pd from"node:fs/promises";import j_ from"node:path";import K2 from"node:os";var L_=()=>process.env.BOSTRAT_STATE_DIR||j_.join(process.env.HOME||K2.tmpdir(),".bostrat"),F_=()=>j_.join(L_(),"dictation-glossary.json"),G2=100,V2=500,z2=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],md=null,Qw=null,eg=Promise.resolve();function N_(){let e={},t=Date.now();for(let n of z2)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function U_(){return md||(Qw??=pd.readFile(F_(),"utf8").then(e=>JSON.parse(e)?.terms??N_()).catch(()=>N_()).then(e=>md=e).finally(()=>Qw=null),Qw)}async function P_(){let e=JSON.stringify({terms:md},null,2),t=F_(),n=`${t}.${process.pid}.tmp`;await pd.mkdir(L_(),{recursive:!0}),await pd.writeFile(n,e),await pd.rename(n,t)}function J2(){return eg=eg.then(P_,P_),eg.catch(()=>{})}async function tg(){return Object.values(await U_()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var Y2=e=>Math.max(1,Math.ceil(e.length/4));async function X2(){let e=[],t=0;for(let{term:n}of await tg()){let r=Y2(n);if(!(t+r>V2)&&(e.push(n),t+=r,e.length>=G2))break}return e}async function Z2(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 D_=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),fd=e=>e.toLowerCase();function B_(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function H_(e,t){let n=D_(e),r=D_(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]=fd(n[u])===fd(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;)fd(n[c])===fd(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(B_(r[l])&&i.add(r[l]),l++);for(;l<s;l++)B_(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await Z2(u,"learned");return await J2(),[...i]}var yc=null;function Q2(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 W_(){if(!yc)return null;let e=await nc();if(!e||e.apiKey.slice(-6)!==yc.keyTail)return null;let{keyTail:t,...n}=yc;return n}async function q_(e,t){let n=await nc();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 X2())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=Q2(o.status,i);c.persistent&&(yc={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 yc=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ue();var ng={"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 eH(e){if(!e)return null;let t=String(e).trim();if(ng[t])return t;let n=null;for(let r of Object.keys(ng))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function tH(e,t={}){let n=ng[eH(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 nH(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 bc=null;async function rH(){if(!bc)return null;let e=await Ku();if(!e||e.apiKey.slice(-6)!==bc.keyTail)return null;let{keyTail:t,...n}=bc;return n}async function oH(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=nH(t.status,o);s.persistent&&(bc={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}bc=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 sH={openai:oH};async function K_(){let e=await Ku();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await rH()}:{configured:!1}}async function G_(){let e=await Ku();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 sH[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function V_(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=tH(t,s),i=await O(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}}Cn();var eI=ke.dirname(iH(import.meta.url)),z_=Number(process.env.PORT)||3362,wd=Xh(process.env),tI=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,la=IE(process.env.BOSTRAT_ALLOW_IPS),ig=$E(process.env.BOSTRAT_VIEW_FRONT_CIDRS,id),nI=OE(),rI=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",v=Lt();v.disable("x-powered-by");v.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),ad(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});v.use((e,t,n)=>{if(mc(e)||la.allowed(la.clientIp(e))||ig.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});v.use(DE);v.use(NE({token:nI,enforce:rI}));v.use("/api/mocks",Lt.json({limit:"1mb"}));v.use("/api/mocks",Lt.raw({type:"image/png",limit:"24mb"}));v.use("/api/mocks",Lt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));v.use("/api/runbooks",Lt.json({limit:"1mb"}));v.use("/api/tracks/adopt",Lt.json({limit:"1mb"}));v.use("/api/telemetry",Lt.json({limit:"256kb"}));v.use("/api/hook",Lt.json({limit:"2mb"}));v.use("/api/dictation",Lt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));v.use("/api/tracks/:id/upload",Lt.raw({type:()=>!0,limit:"25mb"}));v.use(Lt.json({limit:"64kb"}));v.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:xt,hostTmuxAlive:await ir(),autoTriggerPct:tI,plane:ua?ua.status():null})});v.get("/api/sessions",async(e,t)=>{t.json({sessions:await Ut()})});v.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await cr(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});v.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});if(!await fe(n))return t.status(404).json({error:"session not found"});try{await Ht(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});v.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});let r=await fe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await La(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});v.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});try{let r=await xk(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});v.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=kl(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 J_=10;v.get("/api/idle-guard",async(e,t)=>{try{t.json(Yh({tracks:await ie(),views:await lu(),queuedOf:n=>vo(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var jt=_o(),fH=sA({kind:jt}),kc=null,ft=()=>kc&&["auto","prompt","off"].includes(kc)?kc:fH,ag=null,pH=ec.has(jt),oI=async({to:e,from:t})=>!pH||ju(await tc())?!1:(xc.arm({to:e,from:t,kind:jt}),!0);async function mH(e){let t=ft();kc=e??null;let n=ft();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){xc.cancel();return}ag&&await oI(ag)}}var sI=(()=>{try{return dH(import.meta.url)("../package.json").version||null}catch{return null}})(),xc=iA({isBusy:async()=>Yh({tracks:await ie(),views:await lu(),queuedOf:e=>vo(e)}).busy,onStuck:({to:e,from:t})=>bn({current:t,latest:e,installKind:jt,policy:ft(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?bn({current:t,latest:e,installKind:jt,policy:ft(),status:"updating",reason:"auto"}):bn({current:t,latest:e,installKind:jt,policy:ft(),status:"failed",error:n?.reason})).catch(()=>{})});v.get("/api/node/self-update",async(e,t)=>{t.json({installKind:jt,policy:ft(),policySource:kc?"plane":"node",current:sI,status:await tc(),armed:xc.armed})});v.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Lu({to:n,kind:jt,reason:"manual"});if(r.ok)return xc.cancel(),bn({current:r.from,latest:n,installKind:jt,policy:ft(),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 aI(){try{let e=await aA();e&&await bn({current:e.from,latest:e.to,installKind:e.kind||jt,policy:ft(),status:e.status,error:e.error,reason:e.reason}),await hh({current:sI,installKind:jt,policy:ft()})}catch{}}aI();setInterval(aI,3e4).unref();v.get("/api/tracks",async(e,t)=>{let n=(await ie()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>J_){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,J_).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Wi,agentAvailability:await vu(),tickets:await OA()})});v.get("/api/cost",async(e,t)=>{try{let n=await ie();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Cw(n);if(r.cursorUsage=lm(),e.query.format==="text")return t.type("text/plain").send(aE(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/roundup",async(e,t)=>{try{t.json(await lE(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/spend",async(e,t)=>{try{let n=await mE(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=lm(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=ai(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/caps",(e,t)=>{t.json(ai())});v.post("/api/usage/caps",async(e,t)=>{try{t.json(await Ol(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});v.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await SE(await ie(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await kg(e.get(An),{byToken:Ki,legacy:()=>ki(e.body?.session_id,e.body?.cwd)});if(n==="refused")return ga("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return ga("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&&ga("mismatch",{track:r.track.id,box:r.box.name}),ga(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 QA(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&Fc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await Ki(e.get(An));if(!n?.track||!n?.box)return t.status(204).end();t.send((await tE(n.track,n.box)).join(`
|
|
693
|
+
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function w_(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}.${i2(n.host)}/`}function i2(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 gd(t)&&Hm(n[0])?n.slice(1).join("."):t}Ks();th();_u();mm();de();import g_ from"node:os";import{randomUUID as rg}from"node:crypto";var c2=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||g_.hostname(),nodeName:e.BOSTRAT_NODE_NAME||g_.hostname()});var y_=32768,b_=120,k_=1e3,x_=new Set(["claude","codex","cursor"]),l2=new Set(["off","transition","nag"]),u2=[[0,59],[0,23],[1,31],[1,12],[0,6]];function d2(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=u2[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 rn=e=>String(e??"").trim(),Go={name(e){let t=rn(e);if(!t)throw new Error("a loop needs a name");if(t.length>b_)throw new Error(`name is too long (${t.length}); cap is ${b_}`);return t},schedule(e){let t=rn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!d2(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=rn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>y_)throw new Error(`prompt is too long (${t.length} chars); cap is ${y_}`);return t},expectation(e){let t=rn(e)||null;if(t&&t.length>k_)throw new Error(`expectation is too long (${t.length}); cap is ${k_}`);return t},notify(e){let t=rn(e)||"off";if(!l2.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=rn(typeof t=="string"?t:t?.provider)||"claude";if(!x_.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...x_].join(", ")}`);let r=typeof t=="object"&&t&&rn(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 f2(e,t){let n=e&&typeof e=="object"?e:{},r=Go.name(n.name),o=Go.schedule(n.schedule),s=Go.prompt(n.prompt),a=Go.expectation(n.expectation),i=Go.notify(n.notify),c=Go.assignment(n.assignment),l=rn(t?.workspace?.repo)||rn(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}=c2();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:f||null}}async function S_(e,t){let n=f2(t,e),r=rg();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 T_(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await F({id:e.id,cwd:e.cwd}),a=s.find(c=>c.type==="loop.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="loop.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("loopId required \u2014 the loop is created on the plane before the card is answered");let i=a.payload;return await $({id:e.id,cwd:e.cwd},{type:"loop.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:String(r),name:i.name,schedule:i.schedule,provider:i.assignment?.provider??"claude",repo:i.repo??null,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await $({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}var ng=["name","schedule","prompt","expectation","notify","assignment","enabled"],p2=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function E_(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 A_(e,t){let n=rn(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 m2(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=>!ng.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: ${ng.join(", ")}`)}let o={};for(let a of ng){if(!(a in n))continue;let i=Go[a](n[a]);(a==="assignment"?p2(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 __(e,t,n){let r=E_(t),{patch:o,changed:s}=m2(n,r),a=rg(),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 v_=600;async function I_(e,t,n){let r=rn(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>v_)throw new Error(`reason is too long (${r.length}); cap is ${v_}`);let o=E_(t),s=rg(),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 $_=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function R_(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 $_(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 O_(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 $_(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}}lu();de();ur();de();Ln();_u();var C_=10,M_=200,h2=120,w2=24,P_=/^\d{4}-\d{2}-\d{2}$/;function og(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var g2=(e=Date.now())=>new Date(e).toISOString().slice(0,10),pt=(e,t="i")=>new RegExp(e,t),y2=[{re:pt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:pt("\\bSPC\\b",""),reason:"business (SPC)"},{re:pt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:pt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:pt("\\bReplicas\\b",""),reason:"competitor research"},{re:pt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:pt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:pt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],b2=[pt("\\bSPC\\b",""),pt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),pt("competitor|cloudcli|\\bkiro\\b"),pt("\\bReplicas\\b",""),pt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),pt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),pt("(^|[^\\w&])#\\d+\\b"),pt("\\btrack/[\\w.-]+")];function k2({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of y2)if(o.test(n)||o.test(r))return s;return null}function N_(e){let t=String(e||"");for(let n of b2){let r=n.exec(t);if(r)return r[0].trim()}return null}function D_(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 x2=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function v2(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Bi(e);if(t.length)throw new Error(`changelog entry appears to contain a secret VALUE (${t.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of e.split(`
|
|
694
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,h2)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${w2}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=x2.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=N_(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>M_)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${M_}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=N_(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>C_)throw new Error(`${r.length} bullets \u2014 over the ${C_}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function S2(e,{repos:t=og()}={}){let n=new Set(t);return(await xo()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:k2({title:r.title,headRef:r.headRef})}))}async function B_(e,t){let n=await S2(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function j_(e,{md:t,day:n=null}){let r=v2(t),o=n==null||n===""?g2():String(n);if(!P_.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 L_(e,{day:t}){let n=String(t||"");if(!P_.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await $({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}Bs();Ln();Eo();de();Bs();it();Nr();Ln();wi();Ql();vi();Dn();import{execFile as T2}from"node:child_process";import{promisify as E2}from"node:util";var ag=E2(T2),F_=3e4,A2=30*6e4,_2=F_,I2=10*6e4,$2=10080*6e4,R2=60*6e4,O2=8,C2=1440*6e4,M2=3,U_="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",sg=60*6e4,N2=15*6e4,P2=9e4,D2=1440*6e4,bd=new Map,kd=new Map,B2=/could not resolve|not found|no pull requests?/i,j2=e=>B2.test(String(e?.message||e||""));function L2(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<sg)}var H_={fast:_2,slow:I2,decay:R2};function W_(e,t){return e.armedAt&&t-e.armedAt<A2||L2(e,t)||q_(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>$2?"decay":"slow"}function F2(e,t){return H_[W_(e,t)]}function Sc(e,t,n,r){let o=e.lanes?.get(oe(t.repo,t.number))||{};return Zl({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function U2(e,t){Un("watch");try{let{stdout:n}=await ag("gh",["pr","view",String(t),"--repo",e,"--json",U_],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await sn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Rr(n)&&Or(n),n}}async function H2(e){Un("watch");try{let{stdout:t}=await ag("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",U_],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await sn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Rr(t)&&Or(t),t}}async function W2(){let e=new Map;for(let t of await ce().catch(()=>[]))e.set(t.id,t);return e}async function q2(e,t){await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function K2(e,t){let n=await So(e);return n?Ps(n,t):null}async function G2(e,t,n){let r=await So(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ds(r,t,{method:n})}async function V2(e,t){let n=await So(e);if(!n)throw new Error(`no local checkout for ${e}`);return bi(n,t)}async function z2(e,t){await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function da(e,t,n,r,o){if(!await r.disarm(e.repo,e.number).catch(()=>null))return;let a=n.get(e.trackId);a&&await r.emitDisarmed(a,{number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo,reason:o}).catch(()=>{})}var Y2=10*6e4;async function J2(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>C2){await da(e,null,t,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(e.repo,e.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await da(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await da(e,o,t,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(e.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<Y2)return;await da(e,o,t,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(e.repo,e.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(e.repo,e.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await da(e,o,t,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(e.repo,e.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await da(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await K_(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function fa(e,t,n){await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function X2(e,t){try{return await ag("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function vc(e,t,n,r=()=>!0){return(await F(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var pa=(e,t)=>({number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo});function q_(e,t,{grace:n=P2}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=D2}async function Z2(e,t,n,r){let o=r.now??Date.now();if(!q_(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??To)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await F(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??jn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??jn)(e.repo,e.number).catch(()=>null)?(await fa(s,"opened",{...pa(e,t),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${e.repo}#${e.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function Q2(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 ql(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Gl(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<N2;await Mx(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Cx(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await vc(a,"pushed",e.number,f=>f.payload?.sha===s)&&await fa(a,"pushed",{...pa(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>sg)await Kl(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 Kl(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Sc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await vc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await fa(a,"ci-fixed",{...pa(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Kl(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>sg)await Vl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Vl(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Sc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await vc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await fa(a,"reconciled",{...pa(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Vl(e.repo,e.number).catch(()=>{})}}}}async function eH(e,t,n,r){let o=Sc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await vc(s,"closed",e.number)&&await fa(s,"closed",{...pa(e,t),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(e.repo,e.number,"closed").catch(()=>{}),await r.mark(e.repo,e.number,"closed").catch(()=>{})}async function tH(e,t,n){Sc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await vc(r,"lost",e.number)&&await fa(r,"lost",pa(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function K_(e,t,n,r,o={}){let s=Sc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await F(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function nH(e={}){let{now:t=Date.now(),auth:n=Qp,view:r=U2,list:o=H2,links:s=Bx,trackMap:a=W2,emitMerged:i=q2,mark:c=Ir,archiveIfArmed:l=Wn,notifyIfArmed:u=qn,notifyIfResolved:f=ju,followupsIfMerged:d=Bo,batchThreshold:p=M2,detail:h=K2,merge:w=G2,updateBranch:g=V2,lookup:k=rt,disarm:S=hi,emitDisarmed:A=z2,localHasCommit:R=X2,claim:B=jn,midTurn:T=To}=e;if(!(await n().catch(()=>null))?.authenticated||Jl()>0)return 0;let O=await s().catch(()=>[]),I=iv(O),U=O.filter(W=>t-(bd.get(oe(W.repo,W.number))||0)>=F2(W,t));if(!U.length)return 0;let H=Date.now(),ae=new Map,J=new Map;for(let W of[...U].sort(ki)){let be=W_(W,t);ae.set(oe(W.repo,W.number),{lane:be,intervalMs:H_[be]}),bd.set(oe(W.repo,W.number),t),J.has(W.repo)||J.set(W.repo,[]),J.get(W.repo).push(W)}let C=await a().catch(()=>new Map),Q={now:t,lanes:ae,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,followupsIfMerged:d,detail:h,merge:w,updateBranch:g,lookup:k,disarm:S,emitDisarmed:A,localHasCommit:R,claim:B,midTurn:T};for(let[W,be]of J){let se,ue=new Set;if(be.length>p)se=await o(W).catch(()=>null);else{se=new Map;for(let me of be)try{let j=await r(W,me.number);j?.number!=null&&se.set(Number(j.number),j)}catch(j){j2(j)&&ue.add(oe(me.repo,me.number))}}if(se)for(let me of be){let j=oe(me.repo,me.number),ie=se.get(Number(me.number));if(!ie){if(ue.has(j)){let le=(kd.get(j)||0)+1;kd.set(j,le),le>=O2&&(await tH(me,C,Q),kd.delete(j),bd.delete(j))}continue}if(kd.delete(j),ie.state==="MERGED"||ie.state==="CLOSED")ie.state==="MERGED"?await K_(me,ie,C,Q):await eH(me,ie,C,Q),bd.delete(j);else{await Z2(me,ie,C,Q).catch(()=>{}),await Q2(me,ie,C,Q).catch(()=>{});let le=!me.trackId||I.get(me.trackId)===j;me.autoMerge&&le&&await J2(me,C,Q).catch(()=>{})}}}let ve={};for(let{lane:W}of ae.values())ve[W]=(ve[W]||0)+1;return console.log(`[pr-watch] probed ${U.length} link(s) (${Object.entries(ve).map(([W,be])=>`${W} ${be}`).join(", ")}) in ${Date.now()-H}ms`),U.length}function G_(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await nH()}catch{}finally{e=!1}}},F_)}de();Ln();ar();Nr();Dn();function V_(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function rH(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var oH=new Set(["merged","closed","lost"]);function sH(e,t){if(!e?.blocked)return null;let n=V_(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 aH(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function iH(e,t,n,r,o){return(await F(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function cH(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 z_(e,t={}){let{now:n=Date.now(),slugOf:r=Se,lookup:o=rt,note:s=Nx,tracks:a=async()=>(await ce()).filter(f=>!Ge(f)),asked:i=iH,emitChange:c=cH}=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(!rH(p))continue;let h=await o(d,p.number).catch(()=>null);if(!h?.trackId||oH.has(h.state))continue;let{prev:w}=await s(d,p.number,{blocked:V_(p),sha:p.headSha??null,at:n});if(!w)continue;let g=sH(w,p);if(!g||aH(h,g.change))continue;u??=new Map((await a()).map(R=>[R.id,R]));let k=u.get(h.trackId);if(!k)continue;let S=p.headSha??null;if(await i(k,g.change,p.number,S,w.at||0))continue;let A={number:Number(p.number),url:p.url||h.url,title:p.title||h.title,branch:p.headRefName||h.headRef,repo:d,sha:S,delegated:!1,reason:g.reason};g.change==="ci-fixed"?A.passed=p.checks.passed:A.base=p.baseRefName||null,await c(k,g.change,A),l.push({change:g.change,number:p.number,reason:g.reason})}catch{}}return l}lt();de();Nr();Dn();import{randomUUID as lH}from"node:crypto";var uH=2e4,Y_=3,ma=new Map,ig=new Set;async function J_(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 dH(e={}){let{sessions:t=null,tracks:n=null,append:r=$,read:o=F,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Kt(),i=n??await ce()}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=(ma.get(f.name)||0)+1;ma.set(f.name,p),p===Y_&&(ig.add(f.name),await J_(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:lH(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(ma.get(f.name)||0)>=Y_;if(ma.delete(f.name),p||!ig.has(f.name)){ig.add(f.name);let h=await J_(u,f.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ma.keys()])l.has(u)||ma.delete(u)}var Tc=null,cg=!1;function X_(){return Tc?()=>{}:(Tc=setInterval(()=>{cg||(cg=!0,dH().catch(()=>{}).finally(()=>{cg=!1}))},uH),Tc.unref?.(),()=>{clearInterval(Tc),Tc=null})}bo();Km();de();Bs();Eo();bo();import{randomUUID as fH}from"node:crypto";async function pH(){let e=await Promise.resolve().then(()=>(Nr(),SE));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var lg={listRepoPrs:fn,readEvents:F,appendEvent:$,sayDeps:pH,currentBranch:Cr};async function mH(e,t=lg,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Cr)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function ug(e,t,{actions:n,notify:r=!1}={},o=lg){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 mH(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=fH();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 dg(e,{offerId:t,choice:n}={},r=lg){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=Ar.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Os(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,vx(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 nr from"node:fs/promises";import ha from"node:path";import oI from"node:os";import Z_ from"node:fs/promises";import xd from"node:path";import{execFile as hH}from"node:child_process";import{promisify as wH}from"node:util";import{fileURLToPath as gH}from"node:url";var yH=wH(hH),eI=xd.dirname(gH(import.meta.url)),bH=xd.resolve(eI,"..","..","client","dist"),kH="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",xH=new Map([...kH].map((e,t)=>[e,t]));function vH(e){let t=[],n=0,r=0;for(let o of e){let s=xH.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 SH(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=vH(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 TH(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 Q_=new Map;async function EH(e){let t;try{t=await Z_.stat(e)}catch{return null}let n=Q_.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await Z_.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:SH(r.mappings||"")};return Q_.set(e,o),o}catch{return null}}var fg=null;function AH(){return fg||(fg=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):yH("git",["rev-parse","--short","HEAD"],{cwd:xd.resolve(eI,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),fg}var _H=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function IH(e,{distDir:t=bH}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(_H)){let[a,i,c,l]=s,u=await EH(xd.join(t,"assets",`${i}.map`)),f=u?TH(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 tI(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await AH();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await IH(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var $H=()=>process.env.BOSTRAT_STATE_DIR||ha.join(process.env.HOME||oI.tmpdir(),".bostrat"),Qr=()=>ha.join($H(),"telemetry"),sI=()=>ha.join(Qr(),".relay-offsets.json"),RH=50,OH=14,CH=3e4,nI=500,Pt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function MH(e,t){if(!e||typeof e!="object")return null;let n=Pt(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:Pt(e.anonId,64),sessionId:Pt(e.sessionId,64),userId:Pt(e.userId,64),release:Pt(e.release,64),appVersion:Pt(e.appVersion,32),route:Pt(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:Pt(e.error.kind,256),message:Pt(e.error.message,8192),stack:Pt(e.error.stack,32768),fingerprint:Pt(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Pt(s?.n,64),r:Pt(s?.r,128)}))),o}var aI=e=>new Date(e).toISOString().slice(0,10),rI=0;async function iI(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,RH):[]).map(s=>MH(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await tI(s);await nr.mkdir(Qr(),{recursive:!0});let o=ha.join(Qr(),`${aI(t)}.ndjson`);return await nr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
695
|
+
`).join(""),"utf8"),t-rI>6*3600*1e3&&(rI=t,NH(t).catch(()=>{})),r.length}async function NH(e=Date.now()){let t=aI(e-OH*24*3600*1e3),n=[];try{n=await nr.readdir(Qr())}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 nr.rm(ha.join(Qr(),s),{force:!0});let o=await lI();for(let s of r)delete o[s];await uI(o)}var vd=new Map;function cI(e,t=Date.now()){let n=e||"unknown",r=vd.get(n);return r||(vd.size>512&&vd.clear(),r={tokens:40,at:t},vd.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 lI(){try{return JSON.parse(await nr.readFile(sI(),"utf8"))||{}}catch{return{}}}async function uI(e){await nr.mkdir(Qr(),{recursive:!0}),await nr.writeFile(sI(),JSON.stringify(e),"utf8")}function PH(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||oI.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 DH({fetchImpl:e=fetch}={}){let t=await YE();if(!t)return{sent:0,disabled:!0};let n;try{n=(await nr.readdir(Qr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await lI(),o=0;for(let s of n){let a=ha.join(Qr(),s),i=r[s]||0,c;try{let d=await nr.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
696
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=nI){let p=f.slice(d,d+nI).map(w=>PH(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 uI(r)}return{sent:o,disabled:!1}}var pg=null,mg=!1;function dI(){pg||(pg=setInterval(async()=>{if(!mg){mg=!0;try{await DH()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{mg=!1}}},CH),pg.unref?.())}import Td from"node:fs/promises";import wI from"node:path";import BH from"node:os";var gI=()=>process.env.BOSTRAT_STATE_DIR||wI.join(process.env.HOME||BH.tmpdir(),".bostrat"),yI=()=>wI.join(gI(),"dictation-glossary.json"),jH=100,LH=500,FH=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Ed=null,hg=null,wg=Promise.resolve();function fI(){let e={},t=Date.now();for(let n of FH)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function bI(){return Ed||(hg??=Td.readFile(yI(),"utf8").then(e=>JSON.parse(e)?.terms??fI()).catch(()=>fI()).then(e=>Ed=e).finally(()=>hg=null),hg)}async function pI(){let e=JSON.stringify({terms:Ed},null,2),t=yI(),n=`${t}.${process.pid}.tmp`;await Td.mkdir(gI(),{recursive:!0}),await Td.writeFile(n,e),await Td.rename(n,t)}function UH(){return wg=wg.then(pI,pI),wg.catch(()=>{})}async function gg(){return Object.values(await bI()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var HH=e=>Math.max(1,Math.ceil(e.length/4));async function WH(){let e=[],t=0;for(let{term:n}of await gg()){let r=HH(n);if(!(t+r>LH)&&(e.push(n),t+=r,e.length>=jH))break}return e}async function qH(e,t){let n=await bI(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var mI=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Sd=e=>e.toLowerCase();function hI(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function kI(e,t){let n=mI(e),r=mI(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]=Sd(n[u])===Sd(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;)Sd(n[c])===Sd(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(hI(r[l])&&i.add(r[l]),l++);for(;l<s;l++)hI(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await qH(u,"learned");return await UH(),[...i]}var Ec=null;function KH(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 xI(){if(!Ec)return null;let e=await lc();if(!e||e.apiKey.slice(-6)!==Ec.keyTail)return null;let{keyTail:t,...n}=Ec;return n}async function vI(e,t){let n=await lc();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 WH())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=KH(o.status,i);c.persistent&&(Ec={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 Ec=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}de();var yg={"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 GH(e){if(!e)return null;let t=String(e).trim();if(yg[t])return t;let n=null;for(let r of Object.keys(yg))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function VH(e,t={}){let n=yg[GH(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 zH(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 Ac=null;async function YH(){if(!Ac)return null;let e=await nd();if(!e||e.apiKey.slice(-6)!==Ac.keyTail)return null;let{keyTail:t,...n}=Ac;return n}async function JH(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=zH(t.status,o);s.persistent&&(Ac={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}Ac=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 XH={openai:JH};async function SI(){let e=await nd();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await YH()}:{configured:!1}}async function TI(){let e=await nd();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 XH[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function EI(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=VH(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}}Dn();var CI=xe.dirname(QH(import.meta.url)),AI=Number(process.env.PORT)||3362,_d=pw(process.env),MI=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,wa=a_(process.env.BOSTRAT_ALLOW_IPS),Sg=i_(process.env.BOSTRAT_VIEW_FRONT_CIDRS,yd),NI=l_(),PI=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",v=Wt();v.disable("x-powered-by");v.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),gd(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});v.use((e,t,n)=>{if(xc(e)||wa.allowed(wa.clientIp(e))||Sg.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});v.use(m_);v.use(f_({token:NI,enforce:PI}));v.use("/api/mocks",Wt.json({limit:"1mb"}));v.use("/api/mocks",Wt.raw({type:"image/png",limit:"24mb"}));v.use("/api/mocks",Wt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));v.use("/api/runbooks",Wt.json({limit:"1mb"}));v.use("/api/tracks/adopt",Wt.json({limit:"1mb"}));v.use("/api/telemetry",Wt.json({limit:"256kb"}));v.use("/api/hook",Wt.json({limit:"2mb"}));v.use("/api/dictation",Wt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));v.use("/api/tracks/:id/upload",Wt.raw({type:()=>!0,limit:"25mb"}));v.use(Wt.json({limit:"64kb"}));v.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:At,hostTmuxAlive:await fr(),autoTriggerPct:MI,plane:ga?ga.status():null})});v.get("/api/sessions",async(e,t)=>{t.json({sessions:await Kt()})});v.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await pr(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});v.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});if(!await ge(n))return t.status(404).json({error:"session not found"});try{await Gt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});v.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});let r=await ge(n);if(!r)return t.status(404).json({error:"session not found"});let o=await qa(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});v.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});try{let r=await jk(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});v.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=_l(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 _I=10;v.get("/api/idle-guard",async(e,t)=>{try{t.json(fw({tracks:await ce(),views:await bu(),queuedOf:n=>Mo(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Ht=Lo(),oW=BE({kind:Ht}),_c=null,bt=()=>_c&&["auto","prompt","off"].includes(_c)?_c:oW,vg=null,sW=ic.has(Ht),DI=async({to:e,from:t})=>!sW||Yu(await cc())?!1:(Ic.arm({to:e,from:t,kind:Ht}),!0);async function aW(e){let t=bt();_c=e??null;let n=bt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Ic.cancel();return}vg&&await DI(vg)}}var BI=(()=>{try{return rW(import.meta.url)("../package.json").version||null}catch{return null}})(),Ic=LE({isBusy:async()=>fw({tracks:await ce(),views:await bu(),queuedOf:e=>Mo(e)}).busy,onStuck:({to:e,from:t})=>Sn({current:t,latest:e,installKind:Ht,policy:bt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Sn({current:t,latest:e,installKind:Ht,policy:bt(),status:"updating",reason:"auto"}):Sn({current:t,latest:e,installKind:Ht,policy:bt(),status:"failed",error:n?.reason})).catch(()=>{})});v.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Ht,policy:bt(),policySource:_c?"plane":"node",current:BI,status:await cc(),armed:Ic.armed})});v.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Ju({to:n,kind:Ht,reason:"manual"});if(r.ok)return Ic.cancel(),Sn({current:r.from,latest:n,installKind:Ht,policy:bt(),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 jI(){try{let e=await jE();e&&await Sn({current:e.from,latest:e.to,installKind:e.kind||Ht,policy:bt(),status:e.status,error:e.error,reason:e.reason}),await Oh({current:BI,installKind:Ht,policy:bt()})}catch{}}jI();setInterval(jI,3e4).unref();v.get("/api/tracks",async(e,t)=>{let n=(await ce()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>_I){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,_I).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Ji,agentAvailability:await Cu(),tickets:await lA()})});v.get("/api/cost",async(e,t)=>{try{let n=await ce();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await zw(n);if(r.cursorUsage=Em(),e.query.format==="text")return t.type("text/plain").send(jA(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/roundup",async(e,t)=>{try{t.json(await UA(await ce(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/spend",async(e,t)=>{try{let n=await GA(await ce(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Em(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=di(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/caps",(e,t)=>{t.json(di())});v.post("/api/usage/caps",async(e,t)=>{try{t.json(await jl(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});v.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await t_(await ce(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Bg(e.get($n),{byToken:Zi,legacy:()=>_i(e.body?.session_id,e.body?.cwd)});if(n==="refused")return va("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return va("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&&va("mismatch",{track:r.track.id,box:r.box.name}),va(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 OA(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&Vc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await Zi(e.get($n));if(!n?.track||!n?.box)return t.status(204).end();t.send((await MA(n.track,n.box)).join(`
|
|
658
697
|
`)+`
|
|
659
|
-
`)}catch{t.status(204).end()}});v.post("/api/git/committed",async(e,t)=>{try{let n=await Ki(e.get(An));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?ke.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?ke.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+ke.sep))?s:o;nE(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await 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 rE(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});v.get("/api/usage/rework",async(e,t)=>{try{t.json(await hE(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/telemetry",async(e,t)=>{try{if(!R_(la.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await $_(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/repos",async(e,t)=>{try{let n=await En(),r=new Set((await Promise.all(n.map(a=>xe(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await xa()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/github/token",async(e,t)=>{try{t.json(await Oc(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/github/status",(e,t)=>{t.json({configured:Rc(),calls:$x(),latency:Mx()})});v.post("/api/github/refresh",(e,t)=>{Rg(),t.json({ok:!0})});v.get("/api/gmail/search",async(e,t)=>{try{let n=await AE(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await EE(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function cg(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 gd({repo:e=null,number:t=null,branch:n=null}){let r=e?await xe(e):null;if(r&&t!=null){let s=await Qe(r,t),a=s&&await oe(s.trackId);if(a)return a}if(!n)return null;let o=await ie();return Xl(o,n,s=>cg(s,e,r))}async function iI({repo:e=null,number:t=null,branch:n=null}){let r=await gd({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!qe(r))return r;if(!n)return null;let o=e?await xe(e).catch(()=>null):null,s=await ie();return Xl(s,n,a=>!qe(a)&&cg(a,e,o))}async function en(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await gd({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await xe(s).catch(()=>null):null);await O({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 hH(e){let t=(await ie()).filter(n=>!qe(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await xe(n.path);if(r)for(let o of n.prs){if(await Qe(r,o.number))continue;let s=await Xl(t,o.headRefName,a=>cg(a,n.path,r));s&&await St({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}v.get("/api/git/prs",async(e,t)=>{try{let n=await Fx();await hH(n.repos).catch(()=>{}),await w_(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{};try{if(s){let w=o||"merge";if(!pi.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let x=await xe((await jn(n)).path);if(!x)throw new Error("repo has no GitHub remote");let T=await gd({repo:n,number:y,branch:l}).catch(()=>null);if(!T)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await mi(n,y),await St({repo:x,number:y,trackId:T.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ci(x,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await fo(x,y,i===!0),await li(x,y,c===!0&&i!==!0),un(),await en("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:T,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}let f=await Is(n,r,{method:o||"squash"});await en("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await xe(n).catch(()=>null),m=!1;if(p&&(i===!0&&await fo(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await li(p,f.number,!0).catch(()=>{}),await Fn(p,f.number).catch(()=>{}),m=await Ln(p,f.number).catch(()=>!1),await vr(p,f.number,"merged").catch(()=>{})),i===!0&&!m){let w=await gd({repo:n,number:f.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Ao(w.id).catch(()=>{})}t.json(f)}catch(f){let p=f?.message||String(f),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(m).json({error:p})}});v.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await xe((await jn(n)).path).catch(()=>null),l=c?await ui(c,i).catch(()=>null):null;try{await Gp(n,i)}catch(u){if(!l)throw u}un(),await en("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await xe(n).catch(()=>null),s=await Kx(n,{method:r||"merge",onMerged:a=>{en("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Fn(o,a.number).catch(()=>{}).then(()=>Ln(o,a.number)).catch(()=>{}).then(()=>vr(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});v.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:qx()})});v.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!pi.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 po()).filter(f=>f.trackId===c.id&&f.state!=="merged"&&f.state!=="closed").sort(hi);if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let f of l)await fo(f.repo,f.number,o===!0).catch(()=>{}),await li(f.repo,f.number,s===!0&&o!==!0).catch(()=>{});let u=[],d=[];for(let f of l){let p={repo:f.repo,number:f.number,ok:!0,action:"armed"};u.push(p);try{let m=await ho(f.repo);if(!m)throw new Error(`no local checkout for ${f.repo}`);let w=await _s(m,f.number);d.push({r:p,l:f,repoPath:m,pr:w,plan:Ux(w,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:f,l:p,repoPath:m,pr:w,plan:y}of Hx(d))try{if(y.action==="skip")f.action="skipped",f.reason=y.reason;else if(y.action==="merge")await Is(m,p.number,{method:i}),await en("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await Fn(p.repo,p.number).catch(()=>{}),await Ln(p.repo,p.number).catch(()=>{}),await vr(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(y.action==="update-arm"&&(await mi(m,p.number).catch(()=>{}),f.action="updated-armed"),!await ci(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await en("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c,...f.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(x){f.ok=!1,f.action="failed",f.reason=x?.message||String(x)}if(un(),!u.some(f=>f.ok)){if(o===!0)for(let f of l)await fo(f.repo,f.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let f of l)await li(f.repo,f.number,!1).catch(()=>{});return t.status(502).json({error:u.map(f=>`${f.repo}#${f.number}: ${f.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await oe(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await po()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ui(a.repo,a.number).catch(()=>null),c=await ho(a.repo).catch(()=>null);c&&await Gp(c,a.number).catch(()=>{}),i&&(await en("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}))}un(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await oe(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Et(i.workspace),d,f=Array.isArray(a)?a.filter(Boolean):[];if(f.length)d=f.map(w=>{let y=u.find(x=>x.name===w||x.repo===w||x.base===w||x.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(d=await Ms(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of d)try{let y=await zp(w.dir,{title:r,body:o,base:s}),x=w.repo||await xe(w.dir).catch(()=>null);x&&y.number&&await St({repo:x,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await en("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:x??w.name}}),p.push({repo:w.name,slug:x??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?t.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):t.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await zp(i.cwd,{title:r,body:o,base:s}),l=await xe(i.cwd).catch(()=>null);l&&c.number&&await St({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await en("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});v.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ms(n),o=new Map(r.map(a=>[a.base,a])),s=Et(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Eh(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await _h(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});v.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await _s(n,r),s=await xe(n).catch(()=>null),a=await iI({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=Et(a.workspace).find(x=>x.base===n||s&&x.repo===s)??null,c=a.workspace?.kind==="multi",l=await Gx(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ke.join("ci",i.name):"ci",d=ke.join(a.cwd,".bostrat",u),f=ke.join(".bostrat",u,`pr-${o.number}-fail.log`);await at.promises.mkdir(d,{recursive:!0}),await at.promises.writeFile(ke.join(a.cwd,f),l.map(x=>`\u2550\u2550\u2550\u2550 ${x.name}${x.link?` \xB7 ${x.link}`:""} \u2550\u2550\u2550\u2550
|
|
660
|
-
${
|
|
698
|
+
`)}catch{t.status(204).end()}});v.post("/api/git/committed",async(e,t)=>{try{let n=await Zi(e.get($n));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?xe.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?xe.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+xe.sep))?s:o;NA(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await PA(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});v.get("/api/usage/rework",async(e,t)=>{try{t.json(await VA(await ce(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/telemetry",async(e,t)=>{try{if(!cI(wa.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await iI(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/repos",async(e,t)=>{try{let n=await Rn(),r=new Set((await Promise.all(n.map(a=>Se(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Aa()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/github/token",async(e,t)=>{try{t.json(await jc(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/github/status",(e,t)=>{t.json({configured:Bc(),calls:Vx(),latency:Xx()})});v.post("/api/github/refresh",(e,t)=>{Vg(),t.json({ok:!0})});v.get("/api/gmail/search",async(e,t)=>{try{let n=await r_(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await o_(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Tg(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 Id({repo:e=null,number:t=null,branch:n=null}){let r=e?await Se(e):null;if(r&&t!=null){let s=await rt(r,t),a=s&&await te(s.trackId);if(a)return a}if(!n)return null;let o=await ce();return su(o,n,s=>Tg(s,e,r))}async function LI({repo:e=null,number:t=null,branch:n=null}){let r=await Id({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ge(r))return r;if(!n)return null;let o=e?await Se(e).catch(()=>null):null,s=await ce();return su(s,n,a=>!Ge(a)&&Tg(a,e,o))}async function on(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Id({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Se(s).catch(()=>null):null);await $({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function iW(e){let t=(await ce()).filter(n=>!Ge(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Se(n.path);if(r)for(let o of n.prs){if(await rt(r,o.number))continue;let s=await su(t,o.headRefName,a=>Tg(a,n.path,r));s&&await It({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}v.get("/api/git/prs",async(e,t)=>{try{let n=await ov();await iW(n.repos).catch(()=>{}),await z_(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=e.body||{};try{if(s){let w=o||"merge";if(!yi.has(w))throw new Error(`invalid merge method: ${w}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let k=await Se((await Hn(n)).path);if(!k)throw new Error("repo has no GitHub remote");let S=await Id({repo:n,number:g,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await bi(n,g),await It({repo:k,number:g,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await pi(k,g,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await ko(k,g,i===!0),await mi(k,g,c===!0&&i!==!0),mn(),await on("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:g,auto:!0})}let d=await Ds(n,r,{method:o||"squash"});await on("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await Se(n).catch(()=>null),h=!1;if(p&&(i===!0&&await ko(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await mi(p,d.number,!0).catch(()=>{}),await qn(p,d.number).catch(()=>{}),await Bo(p,d.number).catch(()=>{}),h=await Wn(p,d.number).catch(()=>!1),await Ir(p,d.number,"merged").catch(()=>{})),i===!0&&!h){let w=await Id({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Do(w.id).catch(()=>{})}t.json(d)}catch(d){let p=d?.message||String(d),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(h).json({error:p})}});v.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Se((await Hn(n)).path).catch(()=>null),l=c?await hi(c,i).catch(()=>null):null;try{await nm(n,i)}catch(u){if(!l)throw u}mn(),await on("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Se(n).catch(()=>null),s=await lv(n,{method:r||"merge",onMerged:a=>{on("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&qn(o,a.number).catch(()=>{}).then(()=>Bo(o,a.number)).catch(()=>{}).then(()=>Wn(o,a.number)).catch(()=>{}).then(()=>Ir(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});v.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:cv()})});v.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!yi.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await te(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await xo()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed").sort(ki);if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await ko(d.repo,d.number,o===!0).catch(()=>{}),await mi(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let p={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(p);try{let h=await So(d.repo);if(!h)throw new Error(`no local checkout for ${d.repo}`);let w=await Ps(h,d.number);f.push({r:p,l:d,repoPath:h,pr:w,plan:sv(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 av(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await Ds(h,p.number,{method:i}),await on("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await qn(p.repo,p.number).catch(()=>{}),await Bo(p.repo,p.number).catch(()=>{}),await Wn(p.repo,p.number).catch(()=>{}),await Ir(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await bi(h,p.number).catch(()=>{}),d.action="updated-armed"),!await pi(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await on("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){d.ok=!1,d.action="failed",d.reason=k?.message||String(k)}if(mn(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await ko(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await mi(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await te(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await xo()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await hi(a.repo,a.number).catch(()=>null),c=await So(a.repo).catch(()=>null);c&&await nm(c,a.number).catch(()=>{}),i&&(await on("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}))}mn(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await te(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Mt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let g=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!g)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return g});else if(f=await _o(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let g=await om(w.dir,{title:r,body:o,base:s}),k=w.repo||await Se(w.dir).catch(()=>null);k&&g.number&&await It({repo:k,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await on("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 om(i.cwd,{title:r,body:o,base:s}),l=await Se(i.cwd).catch(()=>null);l&&c.number&&await It({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await on("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});v.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await _o(n),o=new Map(r.map(a=>[a.base,a])),s=Mt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Hh(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Wh(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});v.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Ps(n,r),s=await Se(n).catch(()=>null),a=await LI({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=Mt(a.workspace).find(k=>k.base===n||s&&k.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?xe.join("ci",i.name):"ci",f=xe.join(a.cwd,".bostrat",u),d=xe.join(".bostrat",u,`pr-${o.number}-fail.log`);await mt.promises.mkdir(f,{recursive:!0}),await mt.promises.writeFile(xe.join(a.cwd,d),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
699
|
+
${k.log??"(no fetchable log \u2014 see link)"}
|
|
661
700
|
`).join(`
|
|
662
|
-
`));let p=l.map(x=>x.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await Bt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${f} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await St({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await fx(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await en("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(x=>x.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(x=>x.name),log:f})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await _s(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await xe(n).catch(()=>null),a=await iI({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=Et(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 Bt(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 St({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await px(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await en("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=xs.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 oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await F(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 zw(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 Bt(a.id,vs(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 O({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&&xh(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Jw(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 vc=e=>t=>({...t,url:jE(t,e.headers)});v.get("/api/views",async(e,t)=>{try{let n=await lu({trackId:e.query.track||void 0});t.json({views:n.map(vc(e)),maxLive:Rr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/views/:id",async(e,t)=>{let n=await Xv(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:vc(e)(n),session:Ps(n.slug)})});v.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await oe(n):null;if(!i&&s&&(i=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ke.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await _m({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:vc(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});v.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:vc(e)(await Zv(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:vc(e)(await Qv(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/views/:id",async(e,t)=>{try{t.json(await eS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var wH=[["linear",Io,cw],["jira",qu,aw]],rg=new Map;v.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await sw(),r={};for(let[o,s,a]of wH){let i=await s();if(!i){rg.delete(o),r[o]={configured:!1};continue}let c=rg.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},rg.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await sw()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await wA(o),i=n?await oe(n):null;!i&&r&&(i=(await ie()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ke.sep)))||null),i&&await O({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 Po=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await ie()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+ke.sep)))||null),n};v.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await ew()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});v.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Po(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await Xs(o);await Fu(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});v.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Po(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Fu(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var cI=async(e,t,n)=>{if(e)return{ref:e,track:await Po(t,n)};let r=await Po(t,n),o=r?await nw(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};v.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await cI(o,n,r),l=await cA({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});v.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await cI(e.query.circuit,e.query.track,e.query.cwd);t.json(await tw(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});v.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Po(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await dA(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})}});v.get("/api/decisions",async(e,t)=>{try{let n=await Po(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await fA(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});v.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await Po(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await nw(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 lA(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});v.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ke.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 ea(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await oe(r):null;if(!o&&n&&(o=(await ie()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ke.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 CA(),ticket:await xn(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});v.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await xn(e.params.id)})});v.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 ea(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});v.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await kw(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;v.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await DA(e.params.id))}catch(n){let r=n?.message||String(n);t.status(yd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await xw(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(yd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await BA(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(yd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await jA(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(yd(s)).json({error:s})}});v.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Bh(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});v.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ke.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 FA(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await LA()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});v.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await UA(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});v.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await $i({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/mocks/:id",async(e,t)=>{let n=await qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Sc={dotfiles:"allow"};v.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=zt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=_i(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,Sc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});v.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=zt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=_i(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,Sc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});v.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=zt(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(Nt(n,r.v).html,Sc,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});v.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&pu(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 oe(a):null;if(!d&&c&&(d=(await ie()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+ke.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=(d.boxes||[]).find(m=>m.name===d.leadBox),p=await gS({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})}});v.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&pu(n)?n:null;try{let o=await yS(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});v.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await xS(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Bt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await LS(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await vS(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/flows",async(e,t)=>{try{t.json({flows:await NS({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/flows/:id",async(e,t)=>{let n=await PS(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});v.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ke.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await DS({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});v.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await BS(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/flows/:id",async(e,t)=>{try{t.json(await jS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ie(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+ke.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await VS({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/runbooks/:id",async(e,t)=>{let n=await Um(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});v.get("/api/runbooks/:id/md",async(e,t)=>{let n=await Um(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=KS(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 zS(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(zn(n,r.v),Sc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});v.get("/api/loops",async(e,t)=>{t.json(await yi())});v.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ie()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+ke.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await yi();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 nm({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});v.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ke.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 KE(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var lI=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ke.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await yi();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=zE(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};v.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await lI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await JE(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});v.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await lI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await YE(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var uI=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;v.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await ZE(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(uI(a)).json({error:a})}});v.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await QE(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(uI(s)).json({error:s})}});v.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await GE(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});v.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ke.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 JS({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});v.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await YS(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});v.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await XS(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await ZS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/changelog/draft",async(e,t)=>{try{let n=o_(e.query.since||null);t.json({since:n,repos:Hw(),...await s_(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var dI=async({trackId:e,cwd:t})=>{let n=e?await oe(e):null;return!n&&t&&(n=(await ie()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+ke.sep)))||null),n};v.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await dI({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 a_(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});v.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await dI({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 i_(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var gH=120,og=400,ua=null,Y_=null;v.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>og)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${og} cap; a notification is a nudge, put the detail in chat`});let c=Ri(`${n||""}
|
|
663
|
-
${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
|
|
701
|
+
`));let p=l.map(k=>k.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await Ct(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 It({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await $x(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await on("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Ps(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Se(n).catch(()=>null),a=await LI({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=Mt(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 Ct(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 It({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Rx(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await on("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=Ar.filter(f=>e.body.actions.includes(f)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await F(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await ug(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 Ct(a.id,Os(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await $({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&Bh(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await dg(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 $c=e=>t=>({...t,url:w_(t,e.headers)});v.get("/api/views",async(e,t)=>{try{let n=await bu({trackId:e.query.track||void 0});t.json({views:n.map($c(e)),maxLive:Br})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/views/:id",async(e,t)=>{let n=await RS(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:$c(e)(n),session:Hs(n.slug)})});v.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await te(n):null;if(!i&&s&&(i=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Wm({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:$c(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});v.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:$c(e)(await OS(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:$c(e)(await CS(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/views/:id",async(e,t)=>{try{t.json(await MS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var cW=[["linear",Fo,Tw],["jira",td,vw]],bg=new Map;v.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await xw(),r={};for(let[o,s,a]of cW){let i=await s();if(!i){bg.delete(o),r[o]={configured:!1};continue}let c=bg.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},bg.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await xw()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await zE(o),i=n?await te(n):null;!i&&r&&(i=(await ce()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+xe.sep)))||null),i&&await $({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var Vo=async(e,t)=>{let n=e?await te(e):null;return!n&&t&&(n=(await ce()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+xe.sep)))||null),n};v.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await ww()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});v.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Vo(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await oa(o);await Xu(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});v.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Vo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Xu(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var FI=async(e,t,n)=>{if(e)return{ref:e,track:await Vo(t,n)};let r=await Vo(t,n),o=r?await yw(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};v.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await FI(o,n,r),l=await FE({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});v.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await FI(e.query.circuit,e.query.track,e.query.cwd);t.json(await gw(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});v.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Vo(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=await WE(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...f})}catch(u){let f=u?.message||String(u);t.status(/required/i.test(f)?400:502).json({error:f})}});v.get("/api/decisions",async(e,t)=>{try{let n=await Vo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await qE(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});v.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await Vo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await yw(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 UE(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});v.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await ce()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await ia(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await te(r):null;if(!o&&n&&(o=(await ce()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+xe.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await uA(),ticket:await En(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});v.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await En(e.params.id)})});v.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ia(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});v.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Dw(e.params.id)})});var $d=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;v.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await mA(e.params.id))}catch(n){let r=n?.message||String(n);t.status($d(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Bw(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status($d(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await hA(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status($d(r)).json({error:r})}});v.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await wA(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status($d(s)).json({error:s})}});v.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Qh(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});v.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await ce()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await yA(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await gA()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});v.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await te(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await bA(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});v.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Di({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/mocks/:id",async(e,t)=>{let n=await zn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Rc={dotfiles:"allow"};v.get("/api/mocks/:id/png",async(e,t)=>{let n=await zn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Zt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Ni(n,r,"png");if(!o)return t.status(404).json({error:"no image for this version (poster extraction failed)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Rc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});v.get("/api/mocks/:id/media",async(e,t)=>{let n=await zn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Zt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Ni(n,r,"video");if(!o)return t.status(404).json({error:"this version has no video (image or html sourced)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Rc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});v.get("/api/mocks/:id/html",async(e,t)=>{let n=await zn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Zt(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(Ft(n,r.v).html,Rc,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});v.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Su(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await te(a):null;if(!f&&c&&(f=(await ce()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+xe.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(h=>h.name===f.leadBox),p=await JS({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});t.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});v.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Su(n)?n:null;try{let o=await XS(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});v.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await eT(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Ct(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await yT(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await tT(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/flows",async(e,t)=>{try{t.json({flows:await pT({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/flows/:id",async(e,t)=>{let n=await mT(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});v.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await te(n):null;if(!a&&s&&(a=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await hT({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});v.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await wT(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/flows/:id",async(e,t)=>{try{t.json(await gT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ce(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+xe.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await AT({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/runbooks/:id",async(e,t)=>{let n=await rh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});v.get("/api/runbooks/:id/md",async(e,t)=>{let n=await rh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=TT(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 _T(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Zn(n,r.v),Rc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});v.get("/api/loops",async(e,t)=>{t.json(await Si())});v.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ce()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+xe.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Si();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 pm({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});v.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await ce()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await S_(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var Eg=async(e,t)=>{let n=e?await te(e):null;return!n&&t&&(n=(await ce()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+xe.sep)))||null),n};v.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Eg(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 Hv(s,o,{ship:{changedRepos:_o}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});v.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Eg(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 qv(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});v.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await Eg(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:ym,...await Yv(r)})});v.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s}=e.body||{};try{let a=await te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Gv(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Ct,unarchiveTrack:Lu})})}catch(a){let i=a?.message||String(a),c=a?.code==="removed"?410:/unknown offer/i.test(i)?404:/already answered/i.test(i)?409:/required|not on this card/i.test(i)?400:/unreachable/i.test(i)?503:502;t.status(c).json({error:i})}});v.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Wv(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});v.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await te(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Kv(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});ct.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||te(e.trackId).then(async s=>{if(s&&(n&&await Ei(s,n,o),t==="opened"||t==="pushed")){let a=zv(await F(s).catch(()=>[]));a&&await Ei(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var UI=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await te(n):null;if(!s&&r&&(s=(await ce()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Si();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=A_(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};v.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await UI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await __(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});v.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await UI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await I_(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var HI=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;v.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await te(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await R_(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(HI(a)).json({error:a})}});v.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await O_(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(HI(s)).json({error:s})}});v.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await T_(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});v.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await te(n):null;if(!a&&s&&(a=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await IT({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});v.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await $T(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});v.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await RT(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await OT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/changelog/draft",async(e,t)=>{try{let n=D_(e.query.since||null);t.json({since:n,repos:og(),...await B_(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var WI=async({trackId:e,cwd:t})=>{let n=e?await te(e):null;return!n&&t&&(n=(await ce()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+xe.sep)))||null),n};v.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await WI({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 j_(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});v.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await WI({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 L_(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var lW=120,kg=400,ga=null,II=null;v.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>kg)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${kg} cap; a notification is a nudge, put the detail in chat`});let c=Bi(`${n||""}
|
|
702
|
+
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await te(a):null;if(!l&&s&&(l=(await ce()).find(h=>h.cwd&&(s===h.cwd||s.startsWith(h.cwd+xe.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,h)=>String(p).replace(/\s+/g," ").trim().slice(0,h),d={title:n?f(n,lW):l.name,body:f(i,kg),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await $(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),t.json({ok:!0,notification:d,delivered:ga?ga.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});v.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 vI(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});v.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await lc(),terms:await gg(),problem:await xI()})});v.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 kI(n,r)})});v.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await te(e.params.id))return t.status(404).json({error:"track not found"});t.json(await SI())});v.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await te(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 TI())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});v.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await te(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 EI(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});v.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 Bu({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),f=null;if(c)try{await ia(u,c,{source:"create"})}catch(p){f=p?.message||String(p)}let d=null;if(l)try{await Xu(u,await oa(l),{source:"picker"})}catch(p){d=p?.message||String(p)}t.json({ok:!0,track:u,...f?{ticketWarning:f}:{},...d?{circuitWarning:d}:{}})}catch(u){let f=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(f)?400:500).json({error:f})}});v.get("/api/tracks/:id",async(e,t)=>{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:qI(ra(e.params.id)),subtasks:jo(e.params.id).map(Oc),pendingOffers:await Ch(n)})});v.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Vh(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Dw(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})}});v.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await Do(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})}});v.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await Jh(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})}});v.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await Lu(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})}});v.get("/api/tracks/:id/messages",async(e,t)=>{let n=await Mu(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});v.get("/api/tracks/:id/events",async(e,t)=>{let n=await te(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}
|
|
664
703
|
data: ${JSON.stringify(i)}
|
|
665
704
|
|
|
666
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await F(n))i.seq>o&&r(i);let s=
|
|
705
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await F(n))i.seq>o&&r(i);let s=ry(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
667
706
|
|
|
668
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});v.get("/api/tracks/:id/search",async(e,t)=>{let n=await
|
|
669
|
-
`);v.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await
|
|
707
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});v.get("/api/tracks/:id/search",async(e,t)=>{let n=await te(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=ri(await F(n)),a=kl(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}))})});v.get("/api/memory",async(e,t)=>{t.json({stores:await ky(),harvest:wy()})});v.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 xy(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 uW=["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(`
|
|
708
|
+
`);v.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Ct(e.params.id,uW)})}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})}});v.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await Fu(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}:{}})}});v.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r}=e.body||{};try{t.json(await Ct(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})}});v.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await te(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 BT(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})}});v.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await te(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=xe.basename(String(e.params.file||""));if(!r||!ah(xe.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=sh(n.cwd),s,a;try{s=await mt.promises.realpath(o),a=await mt.promises.realpath(xe.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+xe.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 dW=/^[0-9a-fA-F-]{8,64}$/;v.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await te(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(!dW.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=Vt(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=ZH.createInterface({input:mt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let p;try{p=JSON.parse(d)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let f=l?_f(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",f.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(f.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await Du(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})}});v.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await Ph(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)})}});v.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o}=e.body||{};try{t.json(await Nu(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})}});v.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await Kh({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})}});v.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await Nh(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})}});v.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Fh(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})}});v.get("/api/tracks/:id/usage",async(e,t)=>{let n=await te(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 md(r,{fresh:o}))});v.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Uh(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})}});v.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await lw(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Oc(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})}});v.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:jo(e.params.id).map(Oc)})});v.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Mh(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});v.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:Oc(qu(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});v.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:Oc(uw(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Oc(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}v.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await Uu(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})}});v.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await jh(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})}});v.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(qI(ra(e.params.id))??{running:!1})});v.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await rw(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})}});v.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??{}})});v.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await Hu(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})}});v.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await aw({...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 qI(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}v.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:Qc(),paths:Dy,trace:await Py({limit:n})})});var xg=xe.resolve(CI,"..","..","client","dist");mt.existsSync(xg)&&(v.use(Wt.static(xg)),v.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(xe.join(xg,"index.html"),Rc)));var KI=eW.createServer(v),fW=new nW({noServer:!0}),GI=(e,t,n)=>{if(!(xc(e)||wa.allowed(wa.clientIp(e))||Sg.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
670
709
|
\r
|
|
671
|
-
`),t.destroy();return}if(
|
|
710
|
+
`),t.destroy();return}if(h_(e,t,n))return;if(!p_(e,NI,{enforce:PI})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
672
711
|
\r
|
|
673
|
-
`),t.destroy();return}let r=
|
|
712
|
+
`),t.destroy();return}let r=tb(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
674
713
|
\r
|
|
675
714
|
`),t.destroy();return}let o=r.sessionName;if(!Ne(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
676
715
|
\r
|
|
677
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
716
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;fW.handleUpgrade(e,t,n,i=>Zy(i,o,{cols:s,rows:a}))};KI.on("upgrade",GI);var VI=process.env.BOSTRAT_CERT_DIR||xe.join(process.env.HOME||Ad.tmpdir(),".bostrat","certs"),$I=Number(process.env.BOSTRAT_TLS_PORT)||3443,RI=xe.join(VI,"bostrat.globopi.key"),OI=xe.join(VI,"bostrat.globopi.crt");if(mt.existsSync(RI)&&mt.existsSync(OI)){let e=tW.createServer({key:mt.readFileSync(RI),cert:mt.readFileSync(OI)},v);e.on("upgrade",GI),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen($I,_d,()=>{console.log(`[bostrat-agent] https listening on ${_d||"0.0.0.0"}:${$I}`)})}KI.listen(AI,_d,()=>{console.log(`[bostrat-agent] listening on ${_d||"0.0.0.0"}:${AI}`);let e=TE(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: ${At}`),console.log(`[bostrat-agent] auto-trigger default: ${MI}%`),console.log(`[bostrat-agent] ip allowlist: ${wa.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Sg.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),tw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),iw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Xh().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(()=>Zh()).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)),qh().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)),kW(),hW(),wW(),bW(),J0({listTracks:ce,getTrack:te}).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)),HT()&&WT({listTracks:ce}),jy().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)),G_(),X_(),kA({resolveTrack:te}),RA({listTracks:ce,createTrack:Bu,archiveTrack:Do,listRepos:Rn,resolveTrack:te,sayToLead:Ct,answerPrompt:Nu,startLeadHandoff:Uu,interruptLead:Du,listSubtaskJobs:jo,cancelSubtask:qu,readEvents:F,appendEvent:$,divertShip:ug,answerPrExists:dg,knownAgents:Ji}),pW(),Ua().then(t=>{t||console.warn(`[boot] no host tmux server on ${At} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),dI(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(ga=NE({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Ad.hostname(),name:process.env.BOSTRAT_NODE_NAME||Ad.hostname(),listTracks:ce,detectAgents:Cu,sampleMetrics:YA,onTracksTransferred:sw,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||Ad.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(()=>{}),II=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(()=>{}),II=null},onUpdateAvailable:async t=>{try{vg={latest:t.latest,current:t.current},await Sn({...t,policy:bt()}),bt()==="auto"&&await DI({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>aW(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await Sn({current:t,latest:a,min:n,installKind:o,required:!0,policy:bt(),status:"available",reason:"refused"}),bt()!=="off"&&a){let i=await Ju({to:a,kind:Ht,reason:"hard-floor"});i.ok?(Ic.cancel(),await Sn({current:t,latest:a,min:n,installKind:o,required:!0,policy:bt(),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:bt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),os().catch(()=>{}),NT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),xA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),oT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),xT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),jx().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{ga?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function pW(){mW(),yu().catch(()=>{}),setInterval(()=>yu().catch(()=>{}),1e4)}function mW(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-followup","bostrat-ship","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=xe.resolve(CI,"..","bin",e),n=xe.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!mt.existsSync(t))continue;mt.mkdirSync(n,{recursive:!0});let r=xe.join(n,e);(mt.existsSync(r)?mt.readlinkSync(r):null)!==t&&(mt.rmSync(r,{force:!0}),mt.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 hW(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ce(),r=await ZA({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 wW(){let e=null,t=async()=>{try{let n=bs(),r=0,o=[],s=null;try{let l=await ce(),u=await zw(l);r=u.fleet.unpricedTurns||0,o=u.fleet.unpricedModels||[],s=await JA(l,{days:7})}catch{}let a=[...n.problems];s&&!s.ok&&a.push(`${s.divergent} recent row(s) diverge from the table (implied-rate audit) \u2014 run \`npm run cost -- --audit\``);let i=o.filter(l=>l!=="unknown");i.length&&a.push(`${r} unpriced turn(s) \u2014 add rates for: ${i.join(", ")}`);let c=a.join(`
|
|
678
717
|
`);if(!a.length){e!==""&&console.log(`[pricing] table ok \u2014 rates verified ${n.tableVerifiedAt??"never"}`),e="";return}for(let l of a)console.warn(`[pricing] \u26A0 ${l}`);c!==e&&process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: cost instrument needs attention",Tags:"warning"},body:a.join(`
|
|
679
|
-
`).slice(0,900)}).catch(()=>{}),e=c}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var
|
|
718
|
+
`).slice(0,900)}).catch(()=>{}),e=c}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var gW=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,yW=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function bW(){let e=async()=>{try{let n=await by();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await ce()).filter(a=>!Ge(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await yy([...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,gW).unref()},yW).unref()}function kW(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ce();for(let n of t)Ge(n)||!n.boxes?.some(r=>r.state==="live")||(await Mu(n.id).catch(()=>{}),await DS(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|