bostrat-node 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +26 -26
- package/dist/index.js +162 -162
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
1
|
+
var $I=Object.defineProperty;var j=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Ad=(e,t)=>{for(var n in t)$I(e,n,{get:t[n],enumerable:!0})};import{createHash as RI,randomBytes as OI,timingSafeEqual as CI}from"node:crypto";function yg(){return OI(32).toString("hex")}function wa(e){let t=typeof e=="string"?e.trim():"";return t?RI("sha256").update(t).digest("hex"):null}function bg(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:CI(Buffer.from(e),Buffer.from(t))}function ga(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Ed.set(e,(Ed.get(e)||0)+1),_d.has(e)&&n-_d.get(e)<MI)return!1;_d.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=${Ed.get(e)}${o?` ${o}`:""}`),!0}async function kg(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 Tn,An,Ed,_d,MI,Lo=j(()=>{Tn="BOSTRAT_HOOK_TOKEN",An="x-bostrat-hook";Ed=new Map,_d=new Map,MI=6e4});import kt from"node:fs/promises";import xg from"node:os";import mt from"node:path";import{execFile as NI}from"node:child_process";import{promisify as PI}from"node:util";function Ec(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function DI(){return process.env.CLAUDE_CONFIG_DIR?mt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):mt.join(xg.homedir(),".claude.json")}function BI(){return process.env.CLAUDE_CONFIG_DIR?mt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):mt.join(xg.homedir(),".claude","settings.json")}async function Qn(e){if(!e||typeof e!="string")return;let t=mt.resolve(e),n=DI(),r=BI(),o={};try{o=JSON.parse(await kt.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 kt.mkdir(mt.dirname(n),{recursive:!0}),await kt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await kt.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 "${An}: $${Tn}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await kt.mkdir(mt.dirname(r),{recursive:!0}),await kt.writeFile(r,JSON.stringify(i,null,2)),await jI(t)}async function jI(e){try{let t=mt.resolve(e),n=mt.join(t,".bostrat","memory","claude"),r=mt.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await kt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(mt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await kt.mkdir(n,{recursive:!0}),await kt.mkdir(mt.dirname(r),{recursive:!0}),await kt.writeFile(r,JSON.stringify(o,null,2)),await LI(t,["/.claude/settings.local.json"])}catch{}}async function LI(e,t){try{let{stdout:n}=await PI(NI)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=mt.resolve(e,n.trim()),o=await kt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
2
|
`).includes(a));if(!s.length)return;await kt.mkdir(mt.dirname(r),{recursive:!0}),await kt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var ya=j(()=>{Lo()});import Id from"node:fs/promises";import
|
|
6
|
+
`)}catch{}}var ya=j(()=>{Lo()});import Id from"node:fs/promises";import FI from"node:os";import _c from"node:path";function UI(){let e=process.env.CODEX_HOME||_c.join(FI.homedir(),".codex");return _c.join(e,"config.toml")}function vg(e){return`[projects."${e}"]`}function HI(e,t){let n=vg(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 Fo(e){if(!e||typeof e!="string")return;let t=_c.resolve(e),n=UI(),r="";try{r=await Id.readFile(n,"utf8")}catch{}if(HI(r,t))return;let o=vg(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
8
|
`;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await Id.mkdir(_c.dirname(n),{recursive:!0}),await Id.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Ic=j(()=>{});import Sg from"node:fs/promises";import
|
|
14
|
-
`,"utf8")}var $c=j(()=>{});import Rd from"node:fs/promises";import er from"node:path";import{execFile as
|
|
13
|
+
`:"")}try{await Id.mkdir(_c.dirname(n),{recursive:!0}),await Id.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Ic=j(()=>{});import Sg from"node:fs/promises";import WI from"node:os";import ba from"node:path";function qI(e){let n=ba.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||ba.join(WI.homedir(),".cursor");return ba.join(r,"projects",n)}async function Uo(e){if(!e||typeof e!="string")return;let t=ba.resolve(e),n=qI(t),r=ba.join(n,".workspace-trusted");await Sg.mkdir(n,{recursive:!0}),await Sg.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var $c=j(()=>{});import Rd from"node:fs/promises";import er from"node:path";import{execFile as KI}from"node:child_process";import{promisify as GI}from"node:util";async function ZI(e){try{return(await Rd.stat(er.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function QI(e){let t=i=>Rd.readFile(er.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 En(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await ZI(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:er.basename(o),branch:await QI(o)});return}if(s>=YI)return;let i;try{i=await Rd.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||XI.has(c.name)||await t(er.join(o,c.name),s+1)}for(let o of JI)await t(er.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await xe(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 e$(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function xe(e){if(!e)return null;if($d.has(e))return $d.get(e);let t=null;try{let{stdout:n}=await VI("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=e$(n)}catch{t=null}return $d.set(e,t),t}async function Ho(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await En())if((await xe(n.path))?.toLowerCase()===t)return n.path;return null}var VI,Tg,zI,JI,YI,XI,$d,tr=j(()=>{VI=GI(KI),Tg=process.env.HOME||"/home/globo",zI=er.join(process.env.BOSTRAT_STATE_DIR||er.join(Tg,".bostrat"),"repos"),JI=[...(process.env.BOSTRAT_REPOS_ROOT||er.join(Tg,"Documents")).split(","),zI].map(e=>e.trim()).filter(Boolean),YI=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,XI=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);$d=new Map});import t$ from"node:fs";import Ag from"node:fs/promises";import ka from"node:path";import n$ from"node:os";import{fileURLToPath as r$}from"node:url";import{execFile as o$}from"node:child_process";import{promisify as s$}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 Rc(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Oc(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!_g.test(n))throw Wo("repo must be owner/name",400);let r=Od.get(n);if(r&&r.expiresAt-Date.now()>c$)return r;if(!Rc(t))throw Wo("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Ig(`${$e(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:$g(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Wo(`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 Wo(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 Od.set(n,a),a}async function xa({env:e=process.env,force:t=!1}={}){if(!Rc(e))return{configured:!1,repos:[],username:null};if(!t&&Vr&&Date.now()-Vr.at<l$)return Vr.data;try{let n=await Ig(`${$e(e)}/v1/github/repos`,{headers:$g(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Vr={at:Date.now(),data:r},r}catch(n){return Vr?Vr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Rg(){Od.clear(),Vr=null}async function tn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await xe(e):null);if(!r)return{};let{token:o}=await Oc(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Cd({env:e=process.env}={}){return(await xa({env:e})).username??null}function p$(e){let[t,n]=String(e).split("/");return ka.join(i$,t,n)}async function Md(e,{env:t=process.env}={}){let n=(...r)=>Eg("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${d$()}" ${f$(t)}`),await n("config","credential.useHttpPath","true")}async function Cc(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!_g.test(String(e||"")))throw Wo(`bad repo slug "${e}"`,400);let r=p$(e);if(t$.existsSync(ka.join(r,".git")))return r;let{token:o}=await Oc(e,{env:t});await Ag.mkdir(ka.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Eg("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:u$,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Ag.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Wo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Md(r,{env:t}),r}var Eg,a$,i$,_g,c$,l$,u$,Ig,Od,Vr,$g,Wo,d$,f$,it=j(()=>{tr();Eg=s$(o$),a$=process.env.BOSTRAT_STATE_DIR||ka.join(process.env.HOME||n$.tmpdir(),".bostrat"),i$=ka.join(a$,"repos"),_g=/^[^/\s]+\/[^/\s]+$/,c$=5*6e4,l$=6e4,u$=10*6e4,Ig=fetch,Od=new Map,Vr=null;$g=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Wo=(e,t)=>Object.assign(new Error(e),{status:t});d$=()=>r$(new URL("../bin/bostrat-git-credential",import.meta.url)),f$=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Cg(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ng(e,t,n,r=Og){let o=$e(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await h$(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Pg(e,{env:t=process.env,force:n=!1,timeoutMs:r=Og}={}){if(!Cg(t))return{facts:[],source:"unconfigured"};let o=Nd.get(e);if(!n&&o&&Date.now()-o.at<m$)return{facts:o.facts,source:"cache"};try{let s=await Ng(`/v1/memory/${encodeURIComponent(e)}`,{headers:Mg(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 Nd.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 Mc(e,t,{env:n=process.env}={}){if(!Cg(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Ng(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Mg(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 Nd.delete(e),{pushed:s.filter(a=>a.outcome==="accepted").length,rejected:s.filter(a=>a.outcome==="rejected"),source:"plane"}}catch(r){return{pushed:0,source:"unreachable",error:r?.message}}}var m$,Og,Nd,h$,Mg,Dg=j(()=>{it();m$=6e4,Og=5e3,Nd=new Map,h$=(...e)=>fetch(...e);Mg=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Pd from"node:fs/promises";import qo from"node:path";import w$ from"node:os";import{EventEmitter as g$}from"node:events";import{randomUUID as y$}from"node:crypto";function Ko(e){return e?.cwd?qo.join(e.cwd,".bostrat","events"):qo.join(Dd,"events")}async function v$(e,t){if(!Nc.has(t)){let o=(await F(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Nc.set(t,o)}let n=Nc.get(t)+1;return Nc.set(t,n),n}function Bd(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 O(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!b$.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!k$.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!x$.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?qo.join(Dd,"events"):Ko(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let u=e.boxes.find(d=>d.name===t.boxId);o=Bd(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await v$(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||y$(),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 jg(c)&&(c=Lg(c),l=JSON.parse(c)),await Pd.mkdir(r,{recursive:!0}),await Pd.appendFile(Bg(r,n),c+`
|
|
15
15
|
`),ht.emit("event",l),ht.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"?qo.join(Dd,"events"):Ko(e));let o;try{o=await Pd.readFile(Bg(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(jg(a)?Lg(a):a))}catch{}return s}function Fg(e,t){let n=`track:${e}`;return ht.on(n,t),()=>ht.off(n,t)}var b$,k$,x$,ht,Dd,Bg,Nc,S$,jg,Lg,ue=j(()=>{b$=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","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"]),k$=new Set(["user","agent","orchestrator","system","peer"]),x$=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),ht=new g$;ht.setMaxListeners(0);Dd=process.env.BOSTRAT_STATE_DIR||qo.join(process.env.HOME||w$.tmpdir(),".bostrat");Bg=(e,t)=>qo.join(e,`${t}.ndjson`),Nc=new Map;S$=/(?<!\\)((?:\\\\)*)\\u0000/g,jg=e=>e.includes("\\u0000"),Lg=e=>e.replace(S$,"$1\\ufffd")});import z from"node:fs/promises";import T$ from"node:os";import de from"node:path";import{execFile as A$}from"node:child_process";import{promisify as E$}from"node:util";function Dc(){return de.join(Bc,"memory","repos")}function zr(e){return e===_n?de.join(Bc,"memory",_n):de.join(Dc(),e)}async function Sa(e){try{return JSON.parse(await z.readFile(de.join(e,Vg),"utf8"))}catch{return{}}}async function Ld(e,t){let n=await Sa(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(de.join(e,Vg),JSON.stringify(n))}function Hg(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 Wg(e){try{let{stdout:t}=await _$("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Fd(e){if(!e)return[];let t=Hg(await Wg(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=Hg(await Wg(de.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function jc(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 nr(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function nn(e,t=null){let n=t??await z.readFile(e,"utf8").catch(()=>""),{fm:r}=jc(n),o=nr(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 zg(e,t){let n=nr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===_n){let o=nr(e,"trust");if(!o||/^operator/i.test(o))return _n}return t}function Ud(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
18
|
`)){let l=Vo(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=Vo(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
21
|
+
`)}function Jg(e,t){let n=nr(t,"name")||e.replace(/\.md$/,""),r=nr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Ta(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 Yg(e,t){let n=de.join(e,".bostrat","memory"),r=await Ta(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=qg.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let f=de.join(n,d);if(o)try{if((await z.stat(f)).mtimeMs<o-$$)continue}catch{continue}let p;try{p=await z.readFile(f,"utf8")}catch{continue}let{fm:m}=jc(p),w=zg(m,t);if(!w)continue;let y=zr(w),x=de.join(y,d),T=await nn(f,p);if(l.has(y)||l.set(y,await Sa(y)),(l.get(y)[d]||0)>=T)continue;let I=await nn(x).catch(()=>0);if(await z.access(x).then(()=>!0).catch(()=>!1)&&I>=T)continue;await z.mkdir(y,{recursive:!0}),await z.writeFile(x,p),c++;let D=a.get(y);D||a.set(y,D={won:new Set,generated:new Map}),D.won.add(d),D.generated.set(d,Jg(d,m)),i.has(w)||i.set(w,new Set),i.get(w).add(d)}let u=await z.readFile(de.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,f]of a){let p=de.join(d,"MEMORY.md"),m=await z.readFile(p,"utf8").catch(()=>"");await z.writeFile(p,Ud(m,u,f.won,f.generated))}return qg.set(e,s),{harvested:c,changedByKey:i}}async function Xg(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await z.access(de.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await Fd(r),a=await Yg(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 R$(){let e=de.join(Bc,"workspaces"),t=[];try{t=await z.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return Xg(t.filter(n=>n.isDirectory()).map(n=>de.join(e,n.name)))}async function M$(e,t){let n=zr(e),r=await Sa(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!O$.test(l)||l==="MEMORY.md")continue;let u=de.join(n,l),d=await z.access(u).then(()=>!0).catch(()=>!1),f=d?await nn(u):0,p=Number(c.modified)||0;if(c.deleted){p>f&&(d&&await z.rm(u,{force:!0}),await Ld(n,[[l,p]]),d&&(a.push(l),i++));continue}d&&f>=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=de.join(n,"MEMORY.md"),l=await z.readFile(c,"utf8").catch(()=>""),u=Ud(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
23
|
`).filter(f=>!d.has(Vo(f))).join(`
|
|
24
|
-
`)}await z.mkdir(n,{recursive:!0}),await z.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Lc(e,t=null){let n=zr(e),r=t?.length?t:await Ta(n);if(!r.length)return{pushed:0,source:"empty"};let o
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
24
|
+
`)}await z.mkdir(n,{recursive:!0}),await z.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Lc(e,t=null){let n=zr(e),r=t?.length?t:await Ta(n);if(!r.length)return{pushed:0,source:"empty"};let o=C$(await z.readFile(de.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=de.join(n,a),c;try{c=await z.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await nn(i,c),indexLine:o.get(a)??null})}return Mc(e,s)}async function N$(e,{timeoutMs:t}={}){let{facts:n,source:r}=await Pg(e,t?{timeoutMs:t}:{});n.length?await M$(e,n):r==="plane"&&await Lc(e)}function P$(e){let t=e.split(`
|
|
25
|
+
`);t.length>Ug&&(t=t.slice(0,Ug));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>I$&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function B$(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=de.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=zr(u),f=await Sa(d);for(let[y,x]of Object.entries(f)){let T=de.join(n,y);await z.access(T).then(()=>!0).catch(()=>!1)&&await nn(T)<=x&&(await z.rm(T,{force:!0}),o++)}let p=await Ta(d);if(!p.length)continue;await z.mkdir(n,{recursive:!0});for(let y of p){let x=de.join(d,y),T=de.join(n,y),I=await nn(x),$=await nn(T).catch(()=>0);await z.access(T).then(()=>!0).catch(()=>!1)&&$>=I||(await z.copyFile(x,T),r++)}let m=await z.readFile(de.join(d,"MEMORY.md"),"utf8").catch(()=>""),w=u===_n?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(t.length>1?`${w}
|
|
28
28
|
|
|
29
29
|
${m.trim()}`:m.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
|
|
30
30
|
|
|
31
31
|
`)+`
|
|
32
|
-
`,c=
|
|
32
|
+
`,c=P$(i);await z.mkdir(n,{recursive:!0}),await z.writeFile(de.join(n,"MEMORY.md"),c);let l=de.join(n,"claude");return await z.mkdir(l,{recursive:!0}),await z.writeFile(de.join(l,"MEMORY.md"),D$,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Zg(e){try{let t=await Fd(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,_n];return await Promise.all(n.map(r=>N$(r,{timeoutMs:j$}))),await B$(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Qg(){return{...Ve,pending:Go.size,queued:jd.size}}function ey(e){if(Pc.has(e))return Pc.get(e);jd.add(e);let t=Kg.then(async()=>{jd.delete(e),Pc.delete(e);let n=await Wd(e);return Ve.runs++,Ve.lastRunAt=Date.now(),n.error?(Ve.errors++,Ve.lastError=n.error):Ve.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Pc.set(e,t),Kg=t.then(()=>{}),t}function Fc(e,{delayMs:t=L$}={}){if(!e||typeof e!="string")return;Ve.scheduled++;let n=Go.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{Go.delete(e),ey(e)},t);r.unref?.(),Go.set(e,r)}async function Hd(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await z.access(de.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=Go.get(e);r&&(clearTimeout(r),Go.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([ey(e),s])}finally{clearTimeout(o)}}async function ty(e){return va||(va=(async()=>{try{Gg||(Gg=!0,await U$());let{harvested:t,changedByKey:n}=await Xg(e);for(let[r,o]of n)await Lc(r,[...o]);return Ve.reconciles++,Ve.lastReconcileAt=Date.now(),Ve.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return Ve.errors++,Ve.lastError=t?.message||String(t),{harvested:0,error:Ve.lastError}}finally{va=null}})(),va)}function F$(){return de.join(Bc,"memory",".full-sweep-done")}async function ny(){let e=F$();try{let t=JSON.parse(await z.readFile(e,"utf8"));return Ve.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...Ve.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await R$();for(let[o,s]of n)await Lc(o,[...s]);let r={at:Date.now(),harvested:t};return await z.mkdir(de.dirname(e),{recursive:!0}),await z.writeFile(e,JSON.stringify(r)),Ve.backfill=r,{ran:!0,...r}}catch(t){return Ve.errors++,Ve.lastError=t?.message||String(t),{ran:!1,error:Ve.lastError}}}async function U$(){let e=0,t=[];try{t=await z.readdir(Dc(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=de.join(Dc(),n.name),o=zr(_n),s=new Set,a=new Map;for(let p of await Ta(r)){let m=de.join(r,p),w;try{w=await z.readFile(m,"utf8")}catch{continue}let{fm:y}=jc(w);if(zg(y,n.name)!==_n)continue;let x=de.join(o,p),T=await nn(x).catch(()=>0),I=await nn(m,w);await z.mkdir(o,{recursive:!0}),I>T&&await z.writeFile(x,w),await z.rm(m,{force:!0}),s.add(p),a.set(p,Jg(p,y)),e++}if(!s.size)continue;let i=Date.now();await Ld(r,[...s].map(p=>[p,i])),await Mc(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=de.join(r,"MEMORY.md"),l=await z.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
33
|
`).filter(p=>!s.has(Vo(p))).join(`
|
|
34
|
-
`);await z.writeFile(c,u).catch(()=>{});let d=de.join(o,"MEMORY.md"),f=await z.readFile(d,"utf8").catch(()=>"");await z.writeFile(d,Ud(f,l,s,a))}return{moved:e}}async function
|
|
34
|
+
`);await z.writeFile(c,u).catch(()=>{});let d=de.join(o,"MEMORY.md"),f=await z.readFile(d,"utf8").catch(()=>"");await z.writeFile(d,Ud(f,l,s,a))}return{moved:e}}async function ry(){let e=[],t=[_n];try{for(let n of await z.readdir(Dc(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=zr(n),o=await Ta(r),s=await Sa(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await z.readFile(de.join(r,i),"utf8").catch(()=>""),{fm:l}=jc(c);a.push({file:i,name:nr(l,"name")||i.replace(/\.md$/,""),description:nr(l,"description")||"",scope:nr(l,"scope")||null,modified:await nn(de.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function oy(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=zr(e),r=de.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 Ld(n,[[t,s]]);let a=de.join(n,"MEMORY.md"),i=await z.readFile(a,"utf8").catch(()=>"");await z.writeFile(a,i.split(`
|
|
35
35
|
`).filter(l=>Vo(l)!==t).join(`
|
|
36
|
-
`)).catch(()=>{});let c=await Mc(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Wd(e){try{let t=await Fd(e),{harvested:n,changedByKey:r}=await
|
|
37
|
-
`)){let r=Vo(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};
|
|
36
|
+
`)).catch(()=>{});let c=await Mc(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Wd(e){try{let t=await Fd(e),{harvested:n,changedByKey:r}=await Yg(e,t[0]||null);for(let[o,s]of r)await Lc(o,[...s]);return n>0&&await W$(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function W$(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,H$)}}await O({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var _$,Bc,Ug,I$,_n,Vg,Vo,qg,$$,O$,C$,D$,j$,L$,Go,jd,Kg,Ve,Pc,Gg,va,H$,Aa=j(()=>{Dg();ue();_$=E$(A$),Bc=process.env.BOSTRAT_STATE_DIR||de.join(process.env.HOME||T$.tmpdir(),".bostrat"),Ug=200,I$=25*1024;_n="operator";Vg=".tombstones.json";Vo=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;qg=new Map,$$=2e3;O$=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,C$=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=Vo(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};D$=`# 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 qd.rename(r,e)}var
|
|
42
|
+
`;j$=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;L$=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,Go=new Map,jd=new Set,Kg=Promise.resolve(),Ve={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Pc=new Map;Gg=!1,va=null;H$=20});import qd from"node:fs/promises";import q$ from"node:path";async function wt(e,t,{mode:n}={}){await qd.mkdir(q$.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++K$}.tmp`;await qd.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await qd.rename(r,e)}var K$,rr=j(()=>{K$=0});import ly from"node:fs/promises";import uy from"node:os";import dy from"node:path";function Me(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ia(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),G$);try{return await Z$(`${$e(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function py(){return Hc||(Kd||=ly.readFile(fy(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Hc={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>Kd=null),Kd)}async function Wc(){await wt(fy(),Hc).catch(()=>{})}function iy(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Vd(e){let t=await py(),n=iy(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=>iy(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>sy&&(t.ops=t.ops.slice(-sy)),await Wc()}function cy(){Uc||(Uc=setTimeout(()=>{Uc=null,zo().catch(()=>{})},Q$),Uc.unref?.())}function Gd(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function tR(e,t=process.env){if(e.op==="record"){let n=await Ia(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{..._a(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:X$()}]})},t);if(!n.ok)throw Gd(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 ly.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ia(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{..._a(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Gd(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 Ia(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:_a(t)},t);if(!n.ok)throw Gd(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function zo({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Me(e)?Ea||(Ea=(async()=>{let r=await py();nR(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await tR(a,e)}catch(c){if(!c?.refused)return cy(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<z$)return cy(),await Wc(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};t.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>ay&&(r.parked=r.parked.slice(-ay)),s++,await Wc();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await Wc()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Ea=null}),Ea):{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)>=eR);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 ct(e,t,{trackId:n=null,env:r=process.env}={}){return Me(r)?(await Vd({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),zo({env:r})):{queued:!1,source:"unconfigured"}}async function or(e,t,n,r,o,{env:s=process.env}={}){return Me(s)?(await Vd({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),zo({env:s})):{queued:!1,source:"unconfigured"}}async function Ft(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Me(r)?(await Vd({op:"delete",family:e,id:t,deletedAt:n}),zo({env:r})):{queued:!1,source:"unconfigured"}}async function Ye(e,{env:t=process.env}={}){if(!Me(t))return{records:[],source:"unconfigured"};try{let n=await Ia(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:_a(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 $a(e,t,n,r,{env:o=process.env}={}){if(!Me(o))return null;try{let s=await Ia(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:_a(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 G$,V$,sy,z$,ay,J$,Y$,fy,X$,Z$,Q$,eR,_a,Hc,Kd,Uc,Ea,sr=j(()=>{it();rr();G$=1e4,V$=3e4,sy=2e3,z$=5,ay=100,J$=60*6e4,Y$=()=>process.env.BOSTRAT_STATE_DIR||dy.join(process.env.HOME||uy.tmpdir(),".bostrat"),fy=()=>dy.join(Y$(),"content-outbox.json"),X$=()=>process.env.BOSTRAT_NODE_ID||uy.hostname(),Z$=(...e)=>fetch(...e),Q$=V$,eR=J$;_a=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Hc=null,Kd=null,Uc=null,Ea=null});import zd from"node:fs/promises";import my from"node:path";async function hy(e,t,{box:n=null,agent:r=null}={}){if(!e||!Me())return{pushed:!1};try{let o=Date.now();return await ct("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await or("checkpoints",e,o,"md",t),Ra=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function iR(){if(Ra&&Date.now()-Ra.at<aR)return{records:Ra.records,source:"plane"};let e=await Ye("checkpoints");return e.source==="plane"&&(Ra={at:Date.now(),records:e.records}),e}async function qc(e){try{let t=sR(e);if(!t||!Me())return{restored:!1};let n=my.join(e,rR,oR);if(await zd.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await iR();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 $a("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await zd.mkdir(my.dirname(n),{recursive:!0}),await zd.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var rR,oR,sR,aR,Ra,Kc=j(()=>{sr();rR=".bostrat/handoff",oR="latest.md",sR=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;aR=6e4,Ra=null});import Oa from"node:fs/promises";import cR from"node:os";import gy from"node:path";function Jr(e){let t=String(e??"");for(let n of lR)t=t.replace(n,"\u2039redacted\u203A");return t}function Gc(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function uR(e){try{await Oa.mkdir(Jd,{recursive:!0}),await Oa.appendFile(Jo,`${JSON.stringify(e)}
|
|
44
44
|
`,"utf8");let n=(await Oa.readFile(Jo,"utf8")).split(`
|
|
45
|
-
`).filter(Boolean);n.length>
|
|
45
|
+
`).filter(Boolean);n.length>wy&&await Oa.writeFile(Jo,`${n.slice(-wy).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Jo}: ${t?.message||t}`)}}async function ee(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&&Gc())try{t.paneTail=Jr((await Oe(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(Gc()||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
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Jr(o.paneTail)),o}catch{return{raw:Jr(r)}}})}catch{return[]}}var
|
|
49
|
-
`,r=Yd.join(e,t),o=null;try{o=await Vc.readFile(r,"utf8")}catch{}o!==n&&await Vc.writeFile(r,n,{mode:493}),await Vc.chmod(r,493).catch(()=>{})}return e}function
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Jo}: ${t?.message||t}`)}}async function ee(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&&Gc())try{t.paneTail=Jr((await Oe(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(Gc()||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 uR(t)}async function yy({limit:e=80}={}){try{return(await Oa.readFile(Jo,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Jr(o.paneTail)),o}catch{return{raw:Jr(r)}}})}catch{return[]}}var lR,Jd,Jo,wy,by,rn=j(()=>{nt();lR=[/[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];Jd=process.env.BOSTRAT_DEBUG_DIR||gy.join(cR.homedir(),".bostrat","debug"),Jo=gy.join(Jd,"handoff.jsonl"),wy=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;by={DEBUG_DIR:Jd,LOG_FILE:Jo}});import Vc from"node:fs/promises";import Yd from"node:path";import dR from"node:os";async function xy(e=ky){await Vc.mkdir(e,{recursive:!0});for(let t of pR){let n=t==="prepare-commit-msg"?mR:t==="post-commit"?hR:`${Xd}exit 0
|
|
49
|
+
`,r=Yd.join(e,t),o=null;try{o=await Vc.readFile(r,"utf8")}catch{}o!==n&&await Vc.writeFile(r,n,{mode:493}),await Vc.chmod(r,493).catch(()=>{})}return e}function vy({nodeUrl:e,dir:t=ky,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",`${Ca}=${e}`]}function Sy(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Ca,fR,ky,pR,Xd,mR,hR,Zd=j(()=>{Lo();Ca="BOSTRAT_NODE_URL",fR=process.env.BOSTRAT_STATE_DIR||Yd.join(process.env.HOME||dR.tmpdir(),".bostrat"),ky=Yd.join(fR,"git-hooks"),pR=["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"],Xd=`#!/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,7 +61,7 @@ esac
|
|
|
61
61
|
if [ -x "$orig/$hook" ] && [ "$orig/$hook" != "$0" ]; then
|
|
62
62
|
"$orig/$hook" "$@" || exit $?
|
|
63
63
|
fi
|
|
64
|
-
`,
|
|
64
|
+
`,mR=`${Xd}
|
|
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
67
|
[ -z "$${Tn}" ] && exit 0
|
|
@@ -73,7 +73,7 @@ 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
|
+
`,hR=`${Xd}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
78
|
[ -z "$${Tn}" ] && exit 0
|
|
79
79
|
[ -z "$${Ca}" ] && exit 0
|
|
@@ -82,52 +82,52 @@ cwd=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
|
82
82
|
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${Ca}/api/git/committed" \\
|
|
83
83
|
-H 'content-type: application/json' -H "${An}: $${Tn}" --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,f,p,m]=r.split("|"),w=Number(i),y=Number(m);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!
|
|
87
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
88
|
-
`).filter(Boolean).length}catch{return 0}}async function Xr(e,{cols:t=
|
|
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 La(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=Wt(e,t),o=!0,!r)return null}else if(n){if(r=af(e),o=!1,!r)return null}else return null;let s;try{s=
|
|
90
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function Fa(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 Ua(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(
|
|
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 lr(e,t=""){let n=t.trim();return n||df(e)||rs(e)}function ff(e,t){if(e){if(Array.isArray(e)){for(let n of e)ff(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&pf.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))ff(n,t)}}}async 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(ff(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:lr(d.command,d.description),ts:u})}return o.slice(-n)}function
|
|
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=f.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let
|
|
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 d=[];if(u.type==="function_call"&&Ha.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=
|
|
98
|
-
`).trim():"";Ha=new Set(["exec_command","shell","exec"]),wf=new Set(["wait","write_stdin"]),Qr="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 Wa({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&
|
|
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 y=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof y=="string"&&
|
|
101
|
-
|
|
102
|
-
`):""}var
|
|
103
|
-
`);if(!
|
|
104
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
105
|
-
`);return t}function
|
|
106
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
107
|
-
`).pop()||"";return
|
|
108
|
-
`),o=r.findIndex(i=>
|
|
109
|
-
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function
|
|
110
|
-
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function
|
|
111
|
-
`),n=-1;for(let s=t.length-1;s>=0;s--)if(t[s].trim()){n=s;break}if(n<0)return!1;let r=t[n];if(
|
|
112
|
-
`),r=-1;for(let o=n.length-1;o>=0;o--)if(
|
|
113
|
-
`)}function
|
|
114
|
-
`))
|
|
115
|
-
`).slice(-
|
|
116
|
-
`);return
|
|
85
|
+
`});import{execFile as Ay}from"node:child_process";import{promisify as wR}from"node:util";import Jc from"node:fs";import{randomUUID as Ey}from"node:crypto";function Zo(e){return new Promise(t=>setTimeout(t,e))}function zc(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 _y(e){return`/tmp/tmux-${e}/default`}function gR({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=_y(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Na(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Ma}=1`]}function Pa(e,t,n){let r=Yr(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 Yr(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function ar(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function kR(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Yr(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function Iy(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await $y(e,{loginShell:t});return r===Yc?!0:r===Yo?!1:n}async function $y(e,{loginShell:t=!1,timeoutMs:n=Qd}={}){if(!e)return Yo;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Ae(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),Yc}catch(s){return s?.killed||s?.signal?Xo:s?.code===1||s?.code===127?Yo:Xo}}async function Xc(e,{retries:t=xR,sleep:n=vR,probe:r=$y}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Qd});for(let a=0;s===Xo&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Qd*2});return s}function q(...e){return["-S",xt,...e]}async function ir(){if(!Jc.existsSync(xt))return!1;try{return await Ae(W,q("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Da(){if(await ir())return!0;try{Jc.mkdirSync(xt.replace(/\/[^/]+$/,""),{recursive:!0}),await Ae(W,q("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await ir()}function Ne(e){return typeof e=="string"&&SR.test(e)}async function Ut(){if(!Jc.existsSync(xt))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 Ae(W,q("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,f,p,m]=r.split("|"),w=Number(i),y=Number(m);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!bR.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function fe(e){return(await Ut()).find(n=>n.name===e)||null}function ER(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=AR&&TR.test(t)}function Zc(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Ry(e){try{let{stdout:t}=await Ae(W,q("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function $R(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 RR(e,t){let n=Date.now()+Ba(),r=$R(t),o=!1,s=0;for(;;){if(await Zo(_R),s+=1,!await Ry(e))return;let a=await Oe(e,{lines:15}),i=!o&&IR.test(a)&&!!r?.test(a);if(!i&&s>1&&!await In(e))return;if(i){o=!0,await ee({flow:"spawn",phase:"launch-retry",sessionName:e,agent:ar(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Ae(W,q("send-keys","-t",e,"C-u")),await Ae(W,q("send-keys","-t",e,"-l",t)),await Ae(W,q("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await ee({flow:"spawn",phase:"launch-failed",sessionName:e,agent:ar(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function cr(e,t=yR,n={}){if(!ER(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=Zc(r);if(!Ne(o))throw new Error("resulting tmux session name is invalid");if(!await Da())throw new Error(`host tmux server not running on ${xt} and auto-start failed \u2014 start one: ${W} -S ${xt} new-session -d -s keeper "sleep 2147483647"`);if(await Ry(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||ef,a=n.agent||ar(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Ey(),l=i?s:Pa(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Yr(s),f=n.firstRun===!0;f||(d&&await Qn(t),a==="codex"&&await Fo(t),a==="cursor"&&await Uo(t),await Zg(t),n.freshWorkspace!==!0&&await qc(t));let p=d&&!f?Ec():null,m=yg(),w=["-e",`${Tn}=${m}`];w.push(...vy({nodeUrl:Sy()})),p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),d&&w.push(...Na());try{await Ae(W,q("new-session","-d","-s",o,"-c",t,...w))}catch(y){throw zc(p?zc(y,p):y,m)}return await Xr(o),await Ae(W,q("set-option","-t",o,"@bostrat-session","1")),await Ae(W,q("set-option","-t",o,"@bostrat-title",r)),await Ae(W,q("set-option","-t",o,"@bostrat-sid",c)),await Ae(W,q("set-option","-t",o,"@bostrat-agent",a)),await Ae(W,q("send-keys","-t",o,l,"Enter")),kR(l)&&RR(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:m}}async function Ht(e){if(!Ne(e))throw new Error("invalid session name");await Ae(W,q("kill-session","-t",e))}async function Oy(e,t){if(Ne(e))try{let n=t?q("set-option","-t",e,"@bostrat-sid",t):q("set-option","-u","-t",e,"@bostrat-sid");await Ae(W,n)}catch{}}async function Oe(e,{lines:t=40}={}){if(!Ne(e))return"";try{let n=q("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Ae(W,n);return String(r||"")}catch{return""}}async function Cy(e){if(!Ne(e))return 0;try{let{stdout:t}=await Ae(W,q("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
88
|
+
`).filter(Boolean).length}catch{return 0}}async function Xr(e,{cols:t=MR,rows:n=NR}={}){if(!Ne(e))return!0;if(await Cy(e))return!1;try{let r=await My(e);if(r.cols===t&&r.rows===n)return!0;await Ae(W,q("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Ae(W,q("set-window-option","-t",e,"window-size","latest")),await Zo(80)}catch{}return!0}async function PR(e){if(!Ne(e))return 0;try{let{stdout:t}=await Ae(W,q("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function In(e){let t=await PR(e);if(!t)return!1;try{return await Ae("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function My(e){if(!Ne(e))return{cols:0,rows:0};try{let{stdout:t}=await Ae(W,q("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 Qc(e,{cols:t=OR,rows:n=CR}={}){if(Ne(e)&&!await Cy(e))try{let r=await My(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Ae(W,q("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Ae(W,q("set-window-option","-t",e,"window-size","latest")),await Zo(80))}catch{}}async function $n(e){return await Qc(e),Oe(e,{lines:ze})}async function De(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=>DR.has(s));for(let s=0;s<o.length;s++)await Ae(W,q("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Zo(n);return ee({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Qo(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 Ae(W,q("send-keys","-t",e,"-N",String(r),"BSpace")),ee({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function tl(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 Ae(W,q("send-keys","-t",e,"-l",s)),n>0&&await Zo(n);ee({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function BR(e,t){return new Promise((n,r)=>{let o=Ay(W,q(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function jR(e,t){let n=`bostrat-${Ey().slice(0,8)}`;await BR(["load-buffer","-b",n,"-"],t),await Ae(W,q("paste-buffer","-p","-d","-b",n,"-t",e))}async function Je(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 jR(e,t):await Ae(W,q("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Zo(a),await Ae(W,q("send-keys","-t",e,"Enter"))}ee({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 Ae,W,Ty,xt,yR,ef,Ma,tf,bR,Yc,Yo,Xo,Qd,xR,vR,nf,SR,TR,AR,Ba,_R,IR,OR,CR,ze,MR,NR,DR,el,nt=j(()=>{ya();Ic();$c();Aa();Kc();rn();Lo();Zd();Ae=wR(Ay);W=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";Ty=typeof process.getuid=="function"?process.getuid():0,xt=gR({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Jc.existsSync(_y(Ty))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Ty}),yR=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",ef=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ma="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";tf="bostrat-usage-probe",bR=new Set([tf]);Yc="present",Yo="absent",Xo="unknown",Qd=5e3,xR=[250,1e3,3e3],vR=e=>new Promise(t=>setTimeout(t,e));nf={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"};SR=/^[A-Za-z0-9_.-]{1,64}$/;TR=/^[A-Za-z0-9 ._↻-]+$/,AR=64;Ba=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,_R=1e3,IR=/not found|Unknown command/i;OR=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,CR=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,ze=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,MR=Number(process.env.BOSTRAT_BOX_COLS)||120,NR=Number(process.env.BOSTRAT_BOX_ROWS)||40;DR=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);el=1600});import ja from"node:fs";import qR from"node:fs/promises";import es from"node:path";import KR from"node:os";function sf(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function af(e){let t=es.join(of,sf(e)),n;try{n=ja.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=es.join(t,o);try{let{mtimeMs:a}=ja.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Wt(e,t){if(!t)return null;let n=es.join(of,sf(e),`${t}.jsonl`);return ja.existsSync(n)?n:null}function Fy(e){if(!e)return[];let t=es.join(of,sf(e)),n;try{n=ja.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=ja.statSync(es.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function gt(e,t=131072){let n=await qR.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 VR(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 La(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=Wt(e,t),o=!0,!r)return null}else if(n){if(r=af(e),o=!1,!r)return null}else return null;let s;try{s=VR(await gt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Ly,s.model),i=Ly[s.model]||GR;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 Ly,GR,of,qt=j(()=>{Ly={"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},GR=2e5,of=process.env.BOSTRAT_CLAUDE_PROJECTS||es.join(KR.homedir(),".claude","projects")});function zR(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 Fa(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 Ua(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(Wy(o)){t.has(o)&&r++;continue}n.push(o)}return n}function qy(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 XR(e){let t=qy(e),n=rt(Re(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${Be(n)}`:`Read lines ${t.from}\u2013${t.to} of ${Be(n)}`:e.includes("-i")&&n?`Edit ${Be(n)}`:null}function cf(e){if(e.includes("--files")){let s=rt(Re(e,Uy)[0]);return s&&s!=="."?`List files in ${ts(s)}`:"List files"}let t=Re(e,Uy),n=t[0];if(!n)return null;let r=rt(t.slice(1).find(s=>s!==".")),o=ns(n,40);return r?`Search for \u201C${o}\u201D in ${ts(r)}`:`Search for \u201C${o}\u201D`}function lf(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 uf(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 ${Be(n)}`:null}function ZR(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 Hy(e){return e.find(t=>/^https?:\/\//.test(t))||null}function tO(e){let t=Ua(Fa(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Be(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return df(t[2]);let n=Be(t[0]);if(eO.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=QR[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function df(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=zR(t);for(let i=0;i<r.length-1;i++){let c=Ua(Fa(r[i])),l=Ua(Fa(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Be(c[0]))&&Be(l[0])==="sed"){let u=qy(l),d=Re(c)[0];if(u&&d)return ns(u.from===u.to?`Read line ${u.from} of ${Be(d)}`:`Read lines ${u.from}\u2013${u.to} of ${Be(d)}`,88)}}let o=[];for(let i of r){let c=tO(i);c&&o.push({d:c,head:Be(Ua(Fa(i))[0]||""),sub:Re(Ua(Fa(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":ns(s?s.d:o[0].d,88)}var ns,rt,Be,ts,Wy,Uy,JR,YR,QR,eO,Ky=j(()=>{ns=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,rt=e=>e&&!/[$`]/.test(e)?e:null,Be=e=>e.replace(/\/+$/,"").split("/").pop()||e,ts=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},Wy=e=>e.startsWith("-"),Uy=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);JR={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 ${Be(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"},YR={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"};QR={git:e=>{let t=Re(e,new Set(["-C","-c"])),n=JR[t[0]];return n?n(t,e):null},gh:e=>{let t=Re(e),n=YR[t[0]];return n?n(t):null},sed:XR,rg:cf,grep:cf,egrep:cf,nl:e=>{let t=rt(Re(e)[0]);return t?`Read ${Be(t)}`:null},cat:e=>{let t=rt(Re(e)[0]);return t?`Read ${Be(t)}`:null},head:e=>{let t=rt(Re(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Be(t)}`:null},tail:e=>{let t=rt(Re(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Be(t)}`:null},wc:e=>{let t=rt(Re(e)[0]);return t?`Count lines in ${Be(t)}`:"Count input lines"},ls:e=>{let t=rt(Re(e)[0]);return t&&t!=="."?`List files in ${ts(t)}`:"List files"},find:e=>rt(e[1])&&!Wy(e[1])&&e[1]!=="."?`Find files in ${ts(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=rt(Re(e)[0]);return t?`Create directory ${ts(t)}`:null},touch:e=>{let t=rt(Re(e)[0]);return t?`Create ${Be(t)}`:null},rm:e=>{let t=rt(Re(e)[0]);return t?`Delete ${ts(t)}`:null},mv:e=>{let t=rt(Re(e)[0]);return t?`Move ${Be(t)}`:null},cp:e=>{let t=rt(Re(e)[0]);return t?`Copy ${Be(t)}`:null},chmod:e=>{let t=rt(Re(e)[1]);return t?`Change permissions on ${Be(t)}`:null},diff:e=>{let t=Re(e);return t.length>=2?`Compare ${Be(t[0])} and ${Be(t[1])}`:null},curl:e=>{let t=Hy(e);return t?`Fetch ${ns(t,56)}`:"Fetch a URL"},wget:e=>{let t=Hy(e);return t?`Fetch ${ns(t,56)}`:"Fetch a URL"},npm:lf,pnpm:lf,yarn:lf,npx:e=>{let t=Re(e)[0];return t?`Run ${t}`:null},node:e=>uf(e,"Node"),python:e=>uf(e,"Python"),python3:e=>uf(e,"Python"),make:e=>{let t=Re(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:ZR,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}},eO=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function rs(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 lr(e,t=""){let n=t.trim();return n||df(e)||rs(e)}function ff(e,t){if(e){if(Array.isArray(e)){for(let n of e)ff(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&pf.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))ff(n,t)}}}async function Gy(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await gt(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(ff(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:lr(d.command,d.description),ts:u})}return o.slice(-n)}function Vy(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,w)=>w>=s&&(m.ts||0)>(l.ts||0)),d=u===-1?r.length:u,f=r.slice(s,d);if(s=d,!f.length)continue;let p=f.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:p,ts:Math.max(...p.map(m=>m.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var pf,os=j(()=>{qt();Ky();pf=new Set(["Bash"])});import zy from"node:fs";import hf from"node:fs/promises";import mf from"node:path";import nO from"node:os";async function Jy(e){try{return await hf.realpath(e)}catch{return e}}function oO(e=rO){let t=[],n=r=>{let o;try{o=zy.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=mf.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:zy.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function sO(e,t=1048576){let n;try{n=await hf.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 aO(e){let t=await sO(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 Zr(e,t){if(!e&&!t)return null;let n=oO();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Jy(e);for(let o of n){let s=await aO(o.file);if(s&&(s===e||await Jy(s)===r))return o}return null}async function Yy(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Zr(e,t);if(!o)return[];let s;try{s=await gt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
|
|
95
|
+
`)){let p=f.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let T=m.payload?.type;T==="task_started"?(l=!0,u=!1,d=m.payload?.turn_id||null):T==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Qr,sourceId:m.payload?.turn_id||d||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let w=m.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,wf.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=cO(w.content),x=c.length?c.slice():null;c=[],!(!y&&!x)&&a.push({role:"assistant",text:y,tools:x||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}function lO(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 uO(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 dO(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=uO(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Xy(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Zr(e,t);if(!o)return[];let s;try{s=await gt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
97
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&Ha.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=lO(p);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=dO(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((p,m)=>{a.push({id:f?m?`${f}:${m}`:f:null,tool:u.name,command:p,description:"",summary:lr(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Zy(e,t){let n=await Zr(e,t);if(!n)return null;try{return Math.round((await hf.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var rO,iO,cO,Ha,wf,Qr,eo=j(()=>{qt();os();rO=process.env.BOSTRAT_CODEX_SESSIONS||mf.join(process.env.CODEX_HOME||mf.join(nO.homedir(),".codex"),"sessions");iO=new Set(["output_text","text"]),cO=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&iO.has(t.type)).map(t=>t.text).join(`
|
|
98
|
+
`).trim():"";Ha=new Set(["exec_command","shell","exec"]),wf=new Set(["wait","write_stdin"]),Qr="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 Wa({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Qy.has(r))return Qy.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 eb(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Wa({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===Qr?"other":Wa({text:t}),code:null,status:null,requestId:null}}return null}function tb(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Wa({text:n}),code:null,status:fO(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function fO(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var $q,Qy,rl=j(()=>{eo();$q=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Qy=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 pO(e,t){return!e||!t?null:Wt(e,t)}function mO(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 hO(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 wO(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function gf(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 nb(e,t){if(!e.uuid)return null;let n=gf(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 yf(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await gt(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 y=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof y=="string"&&gO.test(y)&&s.set(w.id,y.split("/").pop())}if(wO(l.content)){let w=nb(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=mO(l.content),f=u==="assistant"?hO(l.content):null,p=u==="user"?nb(c,s):null;if(!d&&!f&&!p)continue;let m=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:Wa({error:c.error,status:c.apiErrorStatus,text:d})}:null;o.push({role:u,text:d,tools:f||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:m||void 0})}return o.slice(-n)}async function rb(e,t,{turns:n=16}={}){let r=pO(e,t);if(!r)return"";let o=await yf(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}:** ${yO(s.text||c,700)} ${i}`.trim()}).join(`
|
|
101
|
+
|
|
102
|
+
`):""}var gO,yO,ol=j(()=>{rl();qt();gO=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;yO=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import bO from"node:fs/promises";import{execFile as kO}from"node:child_process";import{promisify as xO}from"node:util";function ib(e){kf=typeof e=="function"?e:null}function xf(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function cb({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||ob.has(r)||(ob.add(r),sb=sb.then(()=>SO({sessionName:e,from:t,to:r})).catch(()=>{}))}async function SO({sessionName:e,from:t,to:n}){await ee({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await bf("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await bO.realpath(r),s=xf(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await bf("npm",["install","-g","@openai/codex"],{env:a,timeout:vO});let i=(await bf(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await ee({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await ab({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await ee({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await ab({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function ab(e){if(kf)try{await kf(e)}catch{}}var bf,vO,ob,sb,kf,sl=j(()=>{rn();bf=xO(kO),vO=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,ob=new Set,sb=Promise.resolve(),kf=null});function AO(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
103
|
+
`);if(!lb.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(TO);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"&&lb.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 to(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
104
|
+
`);for(let r=n.length-1;r>=0;r--)if(fb.test(n[r]))return n.slice(r).join(`
|
|
105
|
+
`);return t}function EO(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 _O(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 IO(e,t=""){let n=to(e);if(qa.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(ub);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let p=r[f].trim();if(p){if(ub.test(r[f])||/^Press enter/.test(p))break;d=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!fb.test(r[l])&&EO(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=_O(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function OO(e,t=""){let n=to(e);if(qa.test(n)||!$O.test(n))return null;let r=n.trimEnd().split(`
|
|
107
|
+
`).pop()||"";return RO.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 MO(e,t=""){let n=to(e);if(qa.test(n)||!db.test(n))return null;let r=n.split(`
|
|
108
|
+
`),o=r.findIndex(i=>db.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(CO);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 NO(e,t=""){let n=to(e);return qa.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 PO(e,t=""){let n=to(e);return qa.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 DO(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||vf.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 jO(e,t=""){let n=String(e);if(!BO.test(n)||vf.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 LO(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||vf.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 pb(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=OO(t,n)||MO(t,n)||NO(t,n)||PO(t,n)||IO(t,n);if(r)return r}if(e==="cursor"){let r=jO(t,n)||LO(t,n)||DO(t,n);if(r)return r}return AO(t,{provider:e,box:n})}function mb(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 TO,lb,qa,fb,ub,$O,RO,db,CO,vf,BO,al=j(()=>{TO=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,lb=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;qa=/(^|\n)\s*›/,fb=/^\s*Welcome to Codex\b/;ub=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;$O=/Successfully logged in\./,RO=/^\s*Press Enter to continue\.\s*$/;db=/Enter this one-time code/,CO=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;vf=/Run Everything|Add a follow-up|Plan, search, build anything/;BO=/click this link to log in|Signing in with the browser/});function WO(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(UO.test(r)||HO.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 zO(e){let t=String(e||"");if(!He.codex.block.test(t))return null;let n=to(t);if(!He.codex.block.test(n))return null;let r=n.match(VO);return{from:r?.[1]||null,to:r?.[2]||null}}function on(e){return new Promise(t=>setTimeout(t,e))}function Sf(e,t){let n=Date.now(),r=JO();for(let[s,a]of cl)n-a>=r&&cl.delete(s);let o=`${e}::${t}`;return cl.has(o)?!1:(cl.set(o,n),!0)}function yb(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(gb.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
113
|
+
`)}function XO(e){let t=0;for(let n of String(e).split(`
|
|
114
|
+
`))gb.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Tf(e,t){let n=He[e];if(!n?.busy)return{state:"idle",reason:null};if(Ef(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(yb(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=XO(t);return e==="claude"&&r>0&&r<YO?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Af(e,t){return Tf(e,t).state}function bb(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
115
|
+
`).slice(-QO).join(`
|
|
116
|
+
`);return ZO.test(n)}function as(e,t){return Af(e,t)==="busy"}function ll(e,t){return Af(e,t)!=="idle"}function is(e,t){let n=He[e];return n?.up?n.up.test(t):!0}function eC(e,t){let n=He[e];return n?Ef(e,t)?!1:n.up.test(t)&&Af(e,t)==="idle":!0}function Ef(e,t){let n=He[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 _f(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 Za(r)}function
|
|
123
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
124
|
-
`):""}async function
|
|
118
|
+
`);return e}function kb(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function hb(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 wb(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await Xr(e).catch(()=>{});;){let l=_f(await Oe(e),o);if(eC(t,l)){await s?.(),await on(ss);return}if(He[t]?.up&&!He[t].up.test(l))if(await In(e)){if(c??=Date.now(),Date.now()-c>=Ba()){let u=hb(t);throw ee({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"&&GO.test(l)&&!He.claude.up.test(l)){ee({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="codex"&&KO.test(l)&&!He.codex.up.test(l)&&i<3){if(!Sf(e,"codex-trust")){await on(il);continue}i+=1,await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await Je(e,"",{enter:!0,kind:"codex-trust-accept"}),await on(ss);continue}if(t==="cursor"&&qO.test(l)&&!He.cursor.up.test(l)&&i<3){if(!Sf(e,"cursor-trust")){await on(il);continue}i+=1,await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await Je(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await on(ss);continue}if(t==="codex"&&i<3){let u=zO(l);if(u){if(!Sf(e,`codex-update:${u.from}->${u.to}`)){await on(il);continue}i+=1,await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await Je(e,"3",{enter:!1,kind:"codex-update-skip"}),cb({sessionName:e,from:u.from,to:u.to}),await on(ss);continue}}if(Date.now()>a){if(Ef(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=He.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"&&He.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"&&He.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"&&He.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'),ee({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await In(e)){let u=hb(t);throw ee({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.`),ee({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!He[t]?.block?.test(l),paneUp:!!He[t]?.up?.test(l),paneBusy:!!He[t]?.busy?.test(yb(He[t],l))}).catch(()=>{}),await on(ss);return}await on(il)}}function If({agent:e,agentCmd:t}={}){let n=t?ar(t):ar(e||"");return{label:n,realClaude:t?Yr(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 xb({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=If({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function Ka({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}={}){let{realCodex:s,realCursor:a}=If({agent:n,agentCmd:r});return!t||!s&&!a?null:ur({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).catch(()=>{})}async function ur({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=FO(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:f}=If({agent:r,agentCmd:o});if(u&&e&&await Qn(e),d&&e&&await Fo(e),f&&e&&await Uo(e),(u||d||f)&&n){await wb(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});return}let p=He[r]?r:He[l]?l:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&p){await wb(n,p,s,{launchEcho:i});return}await on(ss)}var FO,il,ss,UO,HO,He,qO,KO,GO,VO,JO,cl,YO,gb,ZO,QO,Ga=j(()=>{nt();ya();Ic();$c();sl();al();rn();FO=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,il=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ss=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,UO=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,HO=/^\s*tab to queue message\b/;He={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:WO},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/}},qO=/Trust this workspace|\[a\]\s*Trust/,KO=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,GO=/Resume from summary/i,VO=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;JO=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,cl=new Map;YO=68,gb=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;ZO=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,QO=15});function oC(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
119
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=rC}function sn(e,t){return e==="codex"?{enter:!0,submitDelayMs:tC,...oC(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:nC}:{enter:!0}}var tC,nC,rC,Va=j(()=>{tC=(()=>{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})(),rC=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function za(e,t){if(e==="codex")return lC(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(vb.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(vb.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(sC);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 lC(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]){(aC.test(t[o])||iC.test(t[o]))&&!$f.test(t[o])?n=o:$f.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($f);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(cC.test(s))return null;r.unshift(s)}return null}function Rf(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Fe(n.replace(uC,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Fe(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Fe(e){return String(e||"").replace(/\s+/g," ").trim()}function Sb(e,t,{slackMs:n=2e4}={}){let r=Fe(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&&Fe(a.text).includes(r));return s>=0&&s<o.length-1}function Tb(e,t,n,{busy:r=!1}={}){let o=za(e,n);return o&&ul(t,o)||o&&e==="codex"&&Rf(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function ul(e,t){let n=Fe(e),r=Fe(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var vb,sC,$f,aC,iC,cC,uC,dl=j(()=>{vb=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,sC=/^❯[ ]?(.*)$/,$f=/^\s*›[ ]?(.*)$/,aC=/\S+ · \S/,iC=/^\s*(?:\? for shortcuts|tab to queue message)\b/,cC=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;uC=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function Ya({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say"},a={}){if(!dC.has(t))return!0;let i=a.waitForUp??xb({agent:t,agentCmd:n}),c=a.capture||($=>Oe($,{lines:ze})),l=a.pressEnter||($=>De($,["Enter"],{kind:"submit-retry"})),u=a.sleep||hC,d=a.now||Date.now,f=a.log||($=>ee($).catch(()=>{})),p=a.maxMs??mC(),m=d()+p,w={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},y=0,x=0,T=0,I=!1;for(await u(Ja[0]);;){let $=await c(e).catch(()=>""),D=is(t,$),S=as(t,$),A=Tb(t,r,$,{busy:S});if(A==="submitted")return!0;if(i&&(!D||A==="stuck"&&S)){if(d()>=m)return A==="stuck"?(f({...w,phase:"submit-failed",reason:"stuck-at-deadline",retries:y}),!1):(f({...w,phase:"submit-unverified",reason:I?"cli-down":"cli-never-up",retries:y,waitedMs:p}),!0);await u(fC);continue}if(I=!0,A==="stuck"){if(y>=pC)return f({...w,phase:"submit-failed",retries:y}),!1;y+=1,x=0,T=0,await l(e),f({...w,phase:"submit-retry",attempt:y}),await u(Ja[0]);continue}if(x+=1,x>=Ja.length||d()>=m)return!0;T+=1,await u(Ja[Math.min(T,Ja.length-1)])}}var dC,Ja,fC,pC,mC,hC,Of=j(()=>{nt();Ga();dl();rn();dC=new Set(["claude","codex"]),Ja=[250,450,700],fC=500,pC=2,mC=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},hC=e=>new Promise(t=>setTimeout(t,e))});function Rn(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of wC){let o=0;for(let s of gC[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Ab(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))yC.has(r)&&(t+=o||0),bC.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Xa(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 wC,gC,yC,bC,fl=j(()=>{wC=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],gC={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/]};yC=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),bC=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function $b(e){let t=Xa(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||kC.has(r)||n.push(r);return n}function xC(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(Ib(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Ib(n,"assistant",o))}}return t}function Ib(e,t,n){let r=Rn(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 Za(e){let t=xC(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=$b(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,d]of l){let f=n.get(u);f||n.set(u,f=new Map),f.set(i,d)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function pl(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($b(t))];if(!i.length)return[];let c=new Map;for(let u of i){let d=e.postings.get(u);if(!d)continue;let f=d.size,p=Math.log(1+(e.N-f+.5)/(f+.5));for(let[m,w]of d){let y=e.lengths[m],x=w+Eb*(1-_b+_b*(y/(e.avgdl||1))),T=p*(w*(Eb+1)/x);c.set(m,(c.get(m)||0)+T)}}let l=[];for(let[u,d]of c){let f=e.chunks[u];o&&f.role!==o||s&&!f.tags.includes(s)||a&&f.boxId!==a||l.push({chunk:f,score:d})}return l.sort((u,d)=>d.score-u.score||d.chunk.seq-u.chunk.seq),l.slice(0,r)}var Eb,_b,kC,Cf=j(()=>{fl();Eb=1.5,_b=.75,kC=new Set("a an and are as at be been but by for from had has have he her his i if in into is it its of on or our so than that the their them then there these they this those to was were will with would you your we".split(" "))});import ml from"node:fs/promises";import Rb from"node:path";async function vC(e){let t=Rb.join(e,".bostrat","events"),n;try{n=await ml.readdir(t)}catch{return Za([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await ml.readFile(Rb.join(t,o),"utf8");for(let a of s.split(`
|
|
122
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Za(r)}function SC(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 TC(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=pl(e,t,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),d=u.length>800?u.slice(0,800)+"\u2026":u,f=`- **${l}:** ${d}`;if(i+f.length>r||(a.push(f),i+=f.length,a.length>=n))break}return a.length?["",`## ${Ob}, 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 Cb(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await ml.readFile(t,"utf8");if(a.includes(Ob))return{appended:!1,reason:"already-enriched"};let i=await vC(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=TC(i,SC(a),{k:r,maxChars:o,exclude:s});return c?(await ml.appendFile(t,`
|
|
125
125
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
126
|
-
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var
|
|
127
|
-
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function
|
|
126
|
+
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var Ob,Mb=j(()=>{Cf();Ob="Relevant prior discussion (verbatim, retrieved"});function RC(e){return AC.test(String(e||"").trimStart())}function Db(e){let t=String(e||"");if(!RC(t.trimStart()))return null;let n=t.replace(EC,"").trim(),r=OC(n),o=[];for(let u of IC){let d=u.from.map(f=>cs(r.get(f))).find(f=>f&&!Nb(f));d&&o.push({h:u.h,text:Qa(d,_C)})}let s=cs(r.get("Current Work"))||cs(r.get("Primary Request and Intent"))||MC(n),a=wl(s),i=Qa(hl(Pf(a[0]||"")),$C)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Qa(hl(Pf(l)),Nf));for(let u of["Pending Tasks","Optional Next Step"]){let d=wl(cs(r.get(u)))[0]||"";if(!(!d||Nb(d))){c.push(Qa(`Left to do: ${hl(Pf(d))}`,Nf));break}}if(!c.length){let u=wl(cs(r.get("Problem Solving"))||"")[0];u&&c.push(Qa(hl(u),Nf))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function OC(e){let t=new Map,n=[];Mf.lastIndex=0;for(let r=Mf.exec(e);r;r=Mf.exec(e))n.push({name:CC(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 CC(e){return Pb.find(n=>n.toLowerCase()===e.toLowerCase())||e}function cs(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 MC(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return cs(t.split(/\n{2,}/).slice(0,2).join(" "))}function wl(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 Pf(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 hl(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Nb(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function Qa(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=wl(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 AC,Pb,Mf,EC,_C,IC,$C,Nf,Bb=j(()=>{AC=/^This session is being continued from a previous conversation that ran out of context\./,Pb=["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"],Mf=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Pb.join("|")})[:*\t ]*$`,"gim"),EC=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,_C=420,IC=[{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"]}],$C=116,Nf=168});function Lb(e){return Df.has(String(e||"").replace(/^\//,"").toLowerCase())}function gl(e){let t=String(e||"").trim();if(!t)return null;let n=Db(t);if(n)return n;let r=t.match(NC);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(PC);if(o)return{kind:"skill",name:o[1]};let s=t.match(DC);if(s){let i=s[1].toLowerCase();return Df.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(GC);if(a&&Df.has(a[1].toLowerCase()))return{kind:"suppress"};if(BC.test(t))return{kind:"suppress"};if(LC.test(t))return{kind:"suppress"};if(jC.test(t))return{kind:"suppress"};if(FC.test(t)){let i=l=>VC(t.match(l)?.[1].trim()||"")||null,c=i(KC);return{kind:"task",summary:i(UC)||"",taskId:i(HC),toolUseId:i(WC),status:i(qC)?.toLowerCase()||null,resultHead:c?c.length>jb?`${c.slice(0,jb).trimEnd()}\u2026`:c:null}}return null}function VC(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Fb(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 Ub(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var NC,PC,DC,BC,jC,LC,FC,UC,HC,WC,qC,KC,jb,Df,GC,Bf=j(()=>{Bb();NC=/^Base directory for this skill:\s*(\S+)/,PC=/^Run the "([^"]+)" workflow\./,DC=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,BC=/^<local-command-(?:stdout|caveat)>/i,jC=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,LC=/^Continue from where you left off\.$/,FC=/^<task-notification>/i,UC=/<summary>([\s\S]*?)<\/summary>/i,HC=/<task-id>([\s\S]*?)<\/task-id>/i,WC=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,qC=/<status>([\s\S]*?)<\/status>/i,KC=/<result>([\s\S]*?)(?:<\/result>|$)/i,jb=600,Df=new Set(["model","clear","compact","login","exit","quit"]);GC=/^\/([a-z0-9:_-]+)$/i});import zC from"node:crypto";function ZC(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,Wb)){let o=ei(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,JC)){let c=ei(i?.label);if(!c)return null;let l=ei(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<YC)return null;n.push({question:o,header:ei(r?.header).slice(0,XC),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Lf(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Hb.lastIndex=0;let t=null;for(let o;o=Hb.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=ZC(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Ff(e,t,n){let r=zC.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Uf(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Wb}={}){if(!Array.isArray(e)||!t||!jf.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||Lf(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Ff(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 qb(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=ei(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 jf,
|
|
130
|
+
${o}`),s||""}var jf,Wb,JC,YC,XC,Hb,ei,Kb,Hf=j(()=>{jf=new Set(["cursor","codex"]),Wb=4,JC=4,YC=2,XC=24,Hb=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,ei=e=>typeof e=="string"?e.trim():"";Kb=`
|
|
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,72 @@ 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 YC(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 ZC(e,t,n){let r=qb(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 S=new Map,I=new Map,$=null,D=new Map,T=new Map,A=new Map,R=new Map,E=new Map,L=new Map,U=new Map,oe=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),se=s.get(b);return g.some(ae=>{if(!_.startsWith(ae))return!1;let Te=Fe(_.slice(ae.length));return!!Te&&!!se?.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"&&Bb(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:Lb(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(YC(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=Qy(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 se=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}:{},...se.length?{tools:se}:{},...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 se={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(se);else{let Le={id:b.id,kind:"job-dock",dockId:h.id,ts:g,box:b.boxId,agent:b.provider,jobs:[se]};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,XC.has(h.change)&&h.number!=null){let _=ZC(S,I,h);if(S.size>=2){for(let ae of I.values())for(let Te of ae)Te.grouped=!0;g.groupPrs=[...S.values()];let se=[g];for(let ae of S.keys())I.set(ae,se)}else{let se=I.get(_)??[];se.push(g),I.set(_,se)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=qb(h),se=$&&!$.complete||S.size>=2;if(S.has(_))S.delete(_),I.delete(_);else if(h.number!=null)for(let ae of[...S.keys()])ae.endsWith(`#${h.number}`)&&(S.delete(ae),I.delete(ae));if(se&&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=[...S.values()],$.complete=S.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),T.set(h.offerId,g),t.push(g);break}case"pr.offer.answered":{let g=T.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 se=t.findIndex(ae=>ae.kind==="event"&&ae.type==="node-update"&&ae.latest===_.latest);if(se>=0){let ae=t[se];t[se]={..._,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),oe.set(h.offerId,g),t.push(g);break}case"box.fullscreen.answered":{let g=oe.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 se of te.values())se.loopId===h.loopId&&(se.status="stale",se.staleReason="superseded",te.delete(se.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 se=h.questionIndex??0,ae={index:se,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},Te=_.questions.findIndex(Le=>Le.index===se&&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 eM(Ie.sort((b,h)=>(b.ts??0)-(h.ts??0)))}function eM(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<=QC){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:tM(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 Ky(n,r)}function tM(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 Kb(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)+nM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&rM.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Gb(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!sM.has(r.type)||!r.box)continue;let o=(r.ts??0)+oM,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 Vb(e){return(e||[]).filter(t=>aM.has(t?.kind)&&t.status==="open")}var me,XC,qb,QC,nM,rM,oM,sM,aM,qf=j(()=>{os();eo();rl();dl();Bf();Hf();me=e=>e.payload?.ts??e.ts;XC=new Set(["opened","ci-fixed","reconciled","pushed"]),qb=e=>`${e.repo??""}#${e.number}`;QC=2*6e4;nM=12e4,rM=new Set(["archived","unarchived","opened"]);oM=2500,sM=new Set(["pr-opened","pr-merged","pr-auto-armed"]);aM=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","fullscreen-offer"])});function dM(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 pM(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 mM(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<lM&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<uM&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Xb(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=dM(e),s=pM(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(-iM);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Yb[r]??Yb.failover),l.push(""),o.lastPush?.sha){let f=Jb(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=Jb(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=mM(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"}:** ${fM(f.text,cM)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let d=l.join(`
|
|
151
|
-
`);return d.length<=
|
|
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 TM(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(tk.test(s)){if(r.length>=fk)continue;let a=SM(s.replace(tk,"").replace(/[.;,:]+$/,""),pk,xM);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>ek&&(o=`${o.slice(0,ek-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function mk(e,t,{timeoutMs:n=9e4}={}){let r=pr.join(t,bM),o=pr.join(r,kM);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,vM(o)),await xk(o,s,n),TM(await fr.readFile(o,"utf8"))}function hk(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===ck||n===dk;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(ik)||n===wM||r.startsWith(lk)||r.startsWith(uk)}function wk(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-AM;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 gk(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 yk(e,{windows:t=[]}={}){if(hk(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 bk(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,ok,sk)};return Kf.set(e,r),IM(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function IM(e,t){e.step="summarize",await kk(e.name,t),e.step="clear";let n=await fe(e.name);await bl(e.name,n,"/clear"),await zf(EM),e.step="resume",await bl(e.name,n,yM),e.step="done",e.finishedAt=Date.now()}async function kk(e,t,{timeoutMs:n=_M}={}){let r=pr.join(t,ok),o=pr.join(r,sk);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,gM(o)),await xk(o,s,n),await ee({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await yl(o)});let i=await Rb(t,o,{exclude:u=>hk(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:{...ak(u),ts:Date.now()}})}catch{}}await Wd(t);let l=await $M(r,o);return c&&await py(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 kk(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=Xb(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=pr.join(t,nk);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:{...ak(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),nk}async function yl(e){try{return(await fr.stat(e)).mtimeMs}catch{return 0}}async function xk(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 $M(e,t){try{if(!hM.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 rk(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=rk(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:rk(r.agent),text:n,flow:"orchestration-drive"})}var ok,sk,Qb,ik,ck,lk,wM,gM,yM,uk,dk,bM,kM,ek,fk,pk,xM,vM,tk,SM,AM,EM,_M,Kf,nk,Jf=j(()=>{nt();Ga();rn();Va();Of();Ob();Aa();ue();Kc();Zb();ok=".bostrat/handoff",sk="latest.md",Qb=32*1024;ik="Checkpoint: write a work-in-progress handoff",ck="HANDOFF WRITTEN",lk="Resume from .bostrat/handoff/latest.md",wM="/clear",gM=e=>`${ik} 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 ${ck} and take no further action.`,yM=`${lk}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,uk="Beat: write a",dk="BEAT WRITTEN",bM=".bostrat/arc",kM="beat.txt",ek=200,fk=4,pk=5,xM=36,vM=e=>`${uk} 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 ${fk} further lines, each starting with "- " and naming ONE piece in at most ${pk} 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 ${dk} and take no further action.`,tk=/^(?:[-–—*•·]|\d+[.)])\s+/,SM=(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};AM=6e5;EM=3500,_M=3e5,Kf=new Map;nk=".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 Tk(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 OM(e){if(!e)return null;let t=String(e).trim();return mr[t]?t:vk[t]?vk[t]:Object.keys(mr).find(r=>t.startsWith(r))||null}function CM(e){if(!e||e===mr)return RM;let t=Sk.get(e);return t||(t=Tk(e),Sk.set(e,t)),t}function Ak(e,t,n={}){let{table:r,speed:o}=n,s=CM(r),a=r&&r!==mr?s[e]?e:null:OM(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=Ak(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 MM(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 PM({now:e=Date.now(),table:t=mr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...NM,...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=[...MM(t),...PM({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var mr,vk,xl,RM,Sk,Ek,NM,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)."}]},vk={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}$/;RM=Tk(mr);Sk=new WeakMap;Ek=(e,t,n)=>Ak(e,t,n)!==null;NM=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Yf=864e5});function DM(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Ik(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=DM(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&&!Ek(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:S}=Ot(f);i+=S;let I=f.provider||"unknown",$=f.boxId||"unattributed";if(y&&c++,x&&(l++,u.add(m)),a+=p,Ik(d,w),r[m]||={usd:0,tokens:_k(),events:0},r[m].usd+=p,r[m].events+=1,Ik(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 BM(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 $k(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)?BM(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Zf,Rk=j(()=>{gr();Zf=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as jM}from"node:child_process";import{promisify as LM}from"node:util";import{randomUUID as FM}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(HM)}}async function qM(e,t){let n=Ck[t]||Ck.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 Mk(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 Nk(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)||UM;await ee({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await In(e)&&(await qM(e,o),!await Qf(e,a)&&(i=!0,await ee({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Nk(e),!await Qf(e,Ok))))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 Mk(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 Nk(e),!await Qf(e,Ok)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,p=t.resumeSessionId||FM(),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(WM),c&&Na().length&&(await Mk(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,UM,Ok,HM,WM,Ck,Pk=j(()=>{nt();ya();Ic();$c();Va();rn();no=LM(jM),ti=e=>new Promise(t=>setTimeout(t,e)),UM=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Ok=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,HM=250,WM=400,Ck={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 Dk(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 d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let p=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let p=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(p?rs(p):null),detail:p?rs(p):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&
|
|
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 d=r[u].match(oo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!so.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:ao(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
|
|
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(oo);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
|
|
161
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
162
|
-
`),o=mp(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:ao(s.label),description:""}))}]}:null}function
|
|
163
|
-
`),o=mp(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:ao(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
|
|
164
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
165
|
-
`)].reverse().find(a=>dp.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
|
|
166
|
-
`),o=mp(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:ao(s.label),description:""}))}]}:null}function
|
|
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:io.test(n)?!1:
|
|
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 br(e,t){return!e||!t?null:e.agent==="claude"?
|
|
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 si(e,t){if(!t)return 0;try{let{stdout:n}=await je(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function gs(e,t,n,r){return await je(e,["worktree","add","--no-track","-b",n,t,r]),t}async function
|
|
173
|
-
`).map(s=>s.trim()).filter(Boolean)}async function _p(e,t,n,r){let{stdout:o}=await je(e,Tp(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await kp(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await kp(e,o,["--3way","--whitespace=nowarn"]),a=await
|
|
174
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var xp,
|
|
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 F0(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&&!B0.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=j0.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!qN.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ii(e,t,{timeoutMs:n=HN}={}){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(WN)}}async function VN(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 zN(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,M0);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=GN(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=>N0(i)||!M0(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?B0.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=>!N0(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 JN(){let e=C0.join(process.env.CURSOR_CONFIG_DIR||C0.join(UN.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await FN.readFile(e,"utf8"))?.model||null}catch{return null}}async function YN(e,t){await Qc(e),await tl(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await ii(e,P0),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,P0,{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 JN();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 U0(e,t,n){return e==="codex"?zN(t,n):e==="cursor"?YN(t,n):VN(t,n)}var Ml,HN,WN,D0,qN,KN,B0,j0,H0=j(()=>{nt();Va();Ml=e=>new Promise(t=>setTimeout(t,e)),HN=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,WN=300,D0=[{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"}],qN=new Set([...D0.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"]),KN=[{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"}],B0=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),j0=[{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=XN(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function XN(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 K0(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 G0(e){if(!(!e?.id||W0.has(e.id))){W0.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 V0(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,q0,W0,Op,Np=j(()=>{ue();lt=e=>e||null;Nl=Promise.resolve(),Cp=e=>(Nl=Nl.then(e).catch(()=>{}),Nl),q0=()=>Nl;W0=new Set;Op=new Map});function nP(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Q0(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of nP(n).reverse()){if(!ZN.test(r))continue;let o=r.endsWith("?")&&r.length<=tP&&eP.test(r);if(QN.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function tx(e){let t=String(e||""),n=[];rP.test(t)&&n.push("commit"),ex.test(t)&&n.push("rebase"),oP.test(t)&&n.push("push");let r=aP.test(t);!r&&sP.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 nx(e){return ex.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 rx(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 ox(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<=iP&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function sx(e,t){let n=new Set;for(let r of t||[]){if(!r.settled)continue;let o=null;for(let s of e||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to+cP||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function ax(e){let t=(Array.isArray(e)?e:[]).filter(r=>z0[r]).map(r=>z0[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 J0,Y0,X0,Z0,ZN,QN,eP,tP,xs,rP,ex,oP,sP,aP,iP,cP,z0,Ss=j(()=>{J0=/(?<!\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,Y0=/(?<!\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,X0=/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,Z0=/(?<!\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,ZN=new RegExp(`\\b(?:${J0}|${Y0}|${X0}|${Z0})\\b`,"i"),QN=/\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,eP=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,tP=90;xs=["commit","rebase","push","pr"],rP=new RegExp(`\\b(?:${J0})\\b`,"i"),ex=new RegExp(`\\b(?:${Z0})\\b`,"i"),oP=new RegExp(`\\b(?:${Y0})\\b`,"i"),sP=new RegExp(`\\b(?:${X0})\\b`,"i"),aP=/\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;iP=3e4;cP=2500;z0={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import lP from"node:crypto";import Dl from"node:fs/promises";import ix from"node:path";import uP 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[ne(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(cx,{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:lx(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=ne(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:dP.has(a),firstSeen:Date.now()},await Xe(ne(e,t)),{link:i[c],created:!0})}async function Nn(e,t){if(!e||t==null)return null;let n=(await Ue())[ne(e,t)];return!n||n.announced?null:(n.announced=!0,await Xe(ne(e,t)),n)}async function Qe(e,t){return!e||t==null?null:(await Ue())[ne(e,t)]??null}async function vr(e,t,n){if(!e||t==null)return null;let r=(await Ue())[ne(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(ne(e,t)),r):null}async function ux(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[ne(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ne(e,t)),o):null}async function dx(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[ne(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ne(e,t)),o):null}async function Bl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[ne(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Xe(ne(e,t)),r)}async function jl(e,t){if(!e||t==null)return null;let n=(await Ue())[ne(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Xe(ne(e,t)),n)}async function Ll(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[ne(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Xe(ne(e,t)),r)}async function Fl(e,t){if(!e||t==null)return null;let n=(await Ue())[ne(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Xe(ne(e,t)),n)}async function fx(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ue())[ne(e,t)];return r?(r.baseFoldAt=n,await Xe(ne(e,t)),r):null}async function px(e,t){if(!e||t==null)return null;let n=(await Ue())[ne(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Xe(ne(e,t)),n)}async function mx(e,t,n){if(!e||t==null||!n)return null;let r=(await Ue())[ne(e,t)];return r?(r.lastHeadSha=n,await Xe(ne(e,t)),r):null}async function hx(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Ue())[ne(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(ne(e,t))),{prev:o,link:r}}async function ci(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[ne(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Xe(ne(e,t)),r):null}async function fo(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[ne(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Xe(ne(e,t)),r):null}async function li(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[ne(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Xe(ne(e,t)),r):null}async function wx(e,t,n){if(!e||t==null)return null;let r=(await Ue())[ne(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(ne(e,t)),r}async function ui(e,t){if(!e||t==null)return null;let n=(await Ue())[ne(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(ne(e,t)),n)}async function gx(){return Object.values(await Ue()).filter(e=>!fP.has(e.state))}async function po(){return Object.values(await Ue()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function yx(){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:lx(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var cx,Bp,ne,lx,xr,Pp,Dp,dP,fP,Pn=j(()=>{sr();cx=process.env.BOSTRAT_STATE_DIR||ix.join(process.env.HOME||uP.tmpdir(),".bostrat"),Bp=ix.join(cx,"pr-links.json"),ne=(e,t)=>`${e}#${Number(t)}`,lx=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=lP.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},xr=null,Pp=null,Dp=Promise.resolve();dP=new Set(["api","observed","reconciled"]);fP=new Set(["merged","closed","lost"])});import pP from"node:fs";import Lp from"node:fs/promises";import vx from"node:path";import mP from"node:os";function Ax(e,t=!1){if(!t&&e-kx<hP)return;kx=e;let n=JSON.stringify({windowStart:Ts,byCaller:Object.fromEntries(Dn),lastHour:Sr});xx=xx.then(()=>Lp.mkdir(Sx,{recursive:!0})).then(()=>Lp.writeFile(`${Ul}.tmp`,n)).then(()=>Lp.rename(`${Ul}.tmp`,Ul)).catch(()=>{})}function Ex(e){e-Ts<Tx||(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,Ax(e,!0))}function Bn(e="other",t=Date.now()){Ex(t),Dn.set(e,(Dn.get(e)||0)+1),Ax(t)}function _x(e=Date.now()){return Ex(e),{windowStart:Ts,total:Hl(Dn),byCaller:Object.fromEntries(Dn),lastHour:Sr,backoffUntil:mo>e?mo:null}}function Tr(e){return wP.test(String(e?.message||e||""))}function Ar(e,t=Date.now()){return mo<=t&&console.warn(`[github] rate limited \u2014 backing off ${bx/6e4} min:`,String(e?.message||e||"").slice(0,200)),mo=Math.max(mo,t+bx),mo}function Wl(e=Date.now()){return Math.max(0,mo-e)}var Sx,Ul,Tx,bx,hP,Ts,Dn,Sr,mo,kx,Hl,xx,wP,di=j(()=>{Sx=process.env.BOSTRAT_STATE_DIR||vx.join(process.env.HOME||mP.tmpdir(),".bostrat"),Ul=vx.join(Sx,"github-metrics.json"),Tx=60*6e4,bx=5*6e4,hP=6e4,Ts=Date.now(),Dn=new Map,Sr=null,mo=0,kx=0,Hl=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(pP.readFileSync(Ul,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Tx?(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{}xx=Promise.resolve();wP=/rate limit|too quickly|abuse detection|HTTP 429/i});import Ix from"node:fs/promises";import $x from"node:path";import gP 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>bP&&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),kP(l),l}async function kP(e){try{await Ix.mkdir(Rx,{recursive:!0}),await Ix.appendFile(yP,JSON.stringify(e)+`
|
|
179
|
-
`)}catch{}}function Ox(){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 Rx,yP,bP,ql,Fp,Gl=j(()=>{Rx=process.env.BOSTRAT_STATE_DIR||$x.join(process.env.HOME||gP.tmpdir(),".bostrat"),yP=$x.join(Rx,"github-latency.ndjson"),bP=100,ql=[],Fp=new Map});import{execFile as xP}from"node:child_process";import{promisify as vP}from"node:util";async function SP(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,...TP,...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 AP(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 Mx(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(Mx),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 Px(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 Dx(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 Bx(e,t){let n=await Px(e,t);return n===null?{...Up([]),unavailable:!0}:Up(Dx(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 _P(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)<EP}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",Nx]),a=JSON.parse(s);return zl(a,await Bx(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(Cx)}catch(n){if(!Kp(n))throw n;let r=await Ct(e,["pr","list","--json",Nx,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>zl(o,await Bx(e,o.headRefOid))))).sort(Cx)}}function un(){fi.clear()}async function RP(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 jx(){let e=await Wp();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await En())await AP(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?$P:IP;if(i&&(r||n-i.at<c))return i.entry;try{let l=await xe(a.path).catch(()=>null),u=_P(a.path,await cn(a.path,{caller:"tab"}),n);if(await RP(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 fx(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 NP(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 Lx(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 Fx(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 Ux(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 Hx(){let e={};for(let[t,n]of Hp)e[t]={...n};return e}async function Wx(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),DP(s,o,{listPrs:()=>cn(r.path),merge:a=>Is(r.path,a,{method:t}),sleep:MP,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function PP(e,t){let n=Date.now()+(t.settleMaxMs??CP),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(OP)}}async function DP(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 PP(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:NP(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 SP(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 Ct(e,s)}catch(l){throw new Error(ln(l))}finally{un()}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
|
|
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 d.length<=Yb?d:`${d.slice(0,Yb)}
|
|
152
|
+
|
|
153
|
+
[\u2026 brief truncated]`}var uM,dM,Yb,fM,pM,hM,Xb,Zb,ek=j(()=>{qf();uM=12,dM=2e3,Yb=48e3,fM=30,pM=10;hM=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},Xb=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Zb={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 yM from"node:fs";import fr from"node:fs/promises";import pr from"node:path";function ck(e){let t=Jr(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=tk?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,tk).toString("utf8").replace(/�+$/,"")}
|
|
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 d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let p=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let p=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(p?rs(p):null),detail:p?rs(p):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&zM.test(JM(f.content))&&s.add(f.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var zM,JM,Lk=j(()=>{qt();os();zM=/^Async (?:agent|task) launched/i,JM=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 yr(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 YM(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 Fk(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:Kt(`read ${yr(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${ds(r)}`:`Read ${ds(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?yr(t.path,n):null,s=t?.glob?` --glob ${tp(t.glob)}`:"",a=Kt(String(r),40);return{command:Kt(`grep ${tp(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?yr(t.target_directory,n):null;return{command:Kt(`glob ${tp(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Kt(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Kt(`edit ${yr(t.path,n)}`,140),description:`Edit ${ds(t.path)}`}:null;case"Write":return t?.path?{command:Kt(`write ${yr(t.path,n)}`,140),description:`Write ${ds(t.path)}`}:null;case"Delete":return t?.path?{command:Kt(`delete ${yr(t.path,n)}`,140),description:`Delete ${ds(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>yr(s,n)).filter(Boolean);return{command:Kt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=YM(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Kt(`apply_patch ${yr(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${ds(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:Kt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Sl,Kt,ds,tp,Uk=j(()=>{Sl=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Kt=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,ds=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);tp=e=>`"${String(e).replace(/"/g,'\\"')}"`});import ni from"node:fs";import XM from"node:fs/promises";import ro from"node:path";import ZM from"node:os";import QM from"node:crypto";import eN from"node:zlib";function qk(e){let t=QM.createHash("md5").update(String(e)).digest("hex");return ro.join(tN,t)}function Kk(e){try{let t=JSON.parse(ni.readFileSync(ro.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=qk(e),n;try{n=ni.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=ro.join(t,o.name),a=ro.join(s,"store.db");if(!ni.existsSync(a))continue;let i=Kk(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 Tl(e,t){if(!e)return null;if(t){let n=ro.join(qk(e),t),r=ro.join(n,"store.db");if(ni.existsSync(r))return{id:t,dir:n,store:r,...Kk(n)||{createdAtMs:null,updatedAtMs:null}}}return nN(e)}function Hk(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function rN(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=eN.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function oN(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 sN(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Sl.has(r.toolName))continue;let o=Fk(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 np(e,t){if(!Wk)return fs;let{DatabaseSync:n}=Wk,r;try{r=new n(e,{readOnly:!0})}catch{return fs}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 fs;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return fs}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Hk(r,s.latestRootBlobId):null;if(!i)return{...fs,model:a};let c=[],l=[],u=[],d=null,f=[];for(let p of oN(i)){let m=rN(Hk(r,p));if(!m)continue;let w=m.providerOptions?.cursor;if(w?.requestId&&f.push(w.requestId),w?.modelProviderMessageId&&f.push(w.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let T of m.content){let I=T?.providerOptions?.cursor?.modelName;I&&(d=I)}l.push(...sN(m.content,t).map((T,I)=>({...T,id:T.id||`${p}:${I}`}))),u.push(...iN(m.content));let y=aN(m.content);if(!y)continue;let x=[...new Set(u)];u=[],c.push({id:p,text:y,tools:x.length?x:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return fs}finally{try{r.close()}catch{}}}function Gk(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(ni.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 Vk(e,t,{limit:n=80}={}){let r=Tl(e,t);if(!r)return[];let{turns:o,model:s}=await np(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=Gk(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 zk(e,t){let n=Tl(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await np(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 Jk(e,t,{limit:n=80}={}){let r=Tl(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await np(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=Gk(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:lr(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:lr(u.command,u.description),ts:i+1});return l}async function Yk(e,t){let n=Tl(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await XM.stat(n.store)).mtimeMs)}catch{return null}}var tN,aN,iN,Wk,fs,rp=j(()=>{os();Uk();tN=process.env.BOSTRAT_CURSOR_CHATS||ro.join(ZM.homedir(),".cursor","chats");aN=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():"",iN=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],Wk=process.getBuiltinModule?.("node:sqlite"),fs={turns:[],trailingCommands:[],model:null,requestIds:[]}});import cN from"node:fs/promises";function ip(e){return Xk.set(e.agent,e),e}function ri(e){return Xk.get(e)||lN}async function lp(e,t={}){return(await ri(e.agent).readMessages(e,t)).map(cp(e))}async function Zk(e,t={}){return(await ri(e.agent).readCommands(e,t)).map(cp(e))}async function Qk(e,t={}){return(await ri(e.agent).readJobs(e,t)).map(cp(e))}var ps,op,sp,ap,lN,Xk,cp,e0=j(()=>{qt();ol();os();Lk();rp();eo();ps=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}},op=class extends ps{transcript(t){return!t?.cwd||!t?.sessionId?null:Wt(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?yf(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?Gy(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?jk(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await cN.stat(n)).mtimeMs)}catch{return null}}},sp=class extends ps{async readMessages(t,n){return Vk(t.cwd,t.sessionId,n)}async readCommands(t,n){return Jk(t.cwd,t.sessionId,n)}async updatedAt(t){return Yk(t.cwd,t.sessionId)}},ap=class extends ps{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Yy(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Xy(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Zy(t.cwd,this.sessionId(t))}},lN=new ps("unknown"),Xk=new Map;ip(new op("claude",pf));ip(new sp("cursor",Sl));ip(new ap("codex",Ha));cp=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function ao(e){let t=e.search(pp);return(t>=0?e.slice(0,t):e).trim()}function uN(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function dN(e){let t=String(e).trim();return!!(!t||so.test(e)||pp.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function fN(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=pN(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function pN(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(f=>f.length>3&&n.includes(f)).length;if(u)return u;let d=l.slice(0,8).trim();return d.length>=4&&n.includes(d)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function n0(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function mN(e,t=""){let n=String(e);if(!o0.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
158
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(oo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!so.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:ao(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 wN(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||!s0.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(oo);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&&!(hN.test(n[c])||so.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:ao(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 gN(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(oo);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 bN(e,t=""){let n=String(e);if(!up.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
161
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(yN.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let d=r[u].match(oo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!so.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>up.test(u.label)))return null;let i=s.map(u=>({label:ao(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 mp(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(oo);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 kN(e,t=""){let n=String(e);if(!a0.test(n)||!i0.test(n)||io.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=mp(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:ao(s.label),description:""}))}]}:null}function xN(e,t=""){let n=String(e);if(!c0.test(n)||io.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=mp(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:ao(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 SN(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!vN.test(o)||hp.test(e[r]))break;n+=o}return n}function TN(e,t=""){let n=String(e);if(!hp.test(n)||io.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=SN(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 EN(e,t=""){let n=String(e);if(!dp.test(n)||!AN.test(n)||io.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
165
|
+
`)].reverse().find(a=>dp.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 _N(e,t=""){let n=String(e);if(!l0.test(n)||io.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o=mp(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:ao(s.label),description:""}))}]}:null}function IN(e,t=""){let n=String(e);if(io.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(u0);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(so.test(r[l])||pp.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 $N(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(fp.test(e[r])){t=r;break}if(t<0)return null;let n=-1;for(let r=t-1;r>=0;r--)if(/[☐☒]/.test(e[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:t,optStart:n+1,optEnd:t}}function RN(e,t=""){let n=String(e);if(!r0.test(n)||!fp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
|
+
`),o=$N(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let R=i;R<c;R++){let E=r[R].match(oo);E&&(u<0&&(u=R),l.push({line:R,cursor:!!E[1],checked:E[3]!=null&&E[3]!==" ",hasCheckbox:E[3]!=null,label:E[4].trim(),desc:""}))}if(!l.length)return null;for(let R of l)for(let E=R.line+1;E<Math.min(r.length,R.line+4)&&!(oo.test(r[E])||so.test(r[E]));E++){let L=r[E].trim();if(!(!L||dN(r[E])||t0.test(L))){R.desc=uN(L);break}}let d=l.filter(R=>!t0.test(R.label));if(!d.length)return null;let f=d.some(R=>R.hasCheckbox),p=Math.max(0,d.findIndex(R=>R.cursor)),m=s>=0?s+1:0,w=[];for(let R=m;R<u;R++){let E=r[R].trim();E&&!so.test(r[R])&&!/[☐☒]/.test(r[R])&&w.push(E)}let y=w.join(" ")||(s>=0?n0(r[s]):"")||"The agent is asking a question",x=s>=0?fN(r[s],y):null,T=x?.currentTabLabel||(s>=0?n0(r[s]):""),I=d.map(R=>({label:ao(R.label),description:R.desc})),$=I.map(R=>R.label).join("|"),S=`claude:${x?`q${x.questionIndex+1}/${x.questionTotal}`:"q1/1"}:${T}:${y}:${$}`.slice(0,240),A=x?`claude:ask:${x.tabs.map(R=>R.label).join("|")}`.slice(0,240):S;return{provider:"claude",box:t,multiSelect:f,cursorIndex:p,promptId:S,groupId:A,...x?{questionIndex:x.questionIndex,questionTotal:x.questionTotal,currentTabLabel:x.currentTabLabel,tabs:x.tabs}:{},questions:[{question:y,header:T,multiSelect:f,options:I}]}}function co(e,t){let n=String(t||"");return n?e==="claude"?r0.test(n)&&fp.test(n)||o0.test(n)||up.test(n)||s0.test(n.trimEnd().split(`
|
|
169
|
+
`).pop()||"")?!0:io.test(n)?!1:a0.test(n)&&i0.test(n)||c0.test(n)||hp.test(n)||dp.test(n)||l0.test(n)||u0.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 br(e,t){return!e||!t?null:e.agent==="claude"?gN(t,e.name)||RN(t,e.name)||mN(t,e.name)||bN(t,e.name)||EN(t,e.name)||TN(t,e.name)||xN(t,e.name)||kN(t,e.name)||_N(t,e.name)||IN(t,e.name)||wN(t,e.name):e.agent==="cursor"||e.agent==="codex"?pb(e.agent,t,e.name):null}async function wp(e){if(!e)return null;let t=await $n(e.name).catch(()=>"");return br(e,t)}function d0(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 f0(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 r0,fp,oo,t0,so,pp,o0,s0,hN,up,yN,io,a0,i0,c0,hp,vN,dp,AN,l0,u0,gp=j(()=>{nt();al();r0=/to navigate\b.*\bEsc to cancel|Enter to select\b/,fp=/Chat about this/i,oo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,t0=/^(type something|chat about this|submit)\b/i,so=/^[─—-]{6,}\s*$/,pp=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;o0=/^\s*Switch model\?\s*$/m;s0=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,hN=/^\s*[─—▔-]{6,}\s*$/;up=/Resume from summary/i,yN=/^\s*This session is\b/;io=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;a0=/Choose the text style that looks best with your terminal/,i0=/Let's get started\./;c0=/Select login method:/;hp=/Paste code here if prompted/,vN=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;dp=/OAuth error:/,AN=/Press Enter to retry\./;l0=/Quick safety check: Is this a project you created or one you trust/;u0=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Al(e,t,n){if(!e||!t)return null;for(let r of ON)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function MN(e){let t=yp.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},yp.set(e,t)),t}function h0(e,t,n=Date.now()){let r=MN(e);if(r.sentAt=r.sentAt.filter(s=>n-s<m0),r.sentAt.length>=p0)return{action:"give-up"};if(n-r.last<CN)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 w0(e,t=Date.now()){let n=yp.get(e);return n?n.sentAt.filter(r=>t-r<m0).length>=p0:!1}var ON,CN,p0,m0,yp,bp=j(()=>{ON=[{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"]}];CN=5e3,p0=5,m0=3600*1e3,yp=new Map});function ms(e){return typeof e=="string"?e.trim():""}var g0=j(()=>{});import{execFile as NN}from"node:child_process";import{promisify as PN}from"node:util";async function je(e,t,{input:n}={}){let r=xp("git",["-C",e,...t],{maxBuffer:y0});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function hs(e){if(!e)return!1;try{let{stdout:t}=await je(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function b0(e){let{stdout:t}=await je(e,["rev-parse","--show-toplevel"]);return t.trim()}async function DN(e){try{return await je(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ws(e,t="bostrat: subtask snapshot"){let{stdout:n}=await je(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>xp("git",["-C",e,...a],{maxBuffer:y0,env:o,...i});try{let a=await DN(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 xp("rm",["-f",r])}catch{}}}async function vp(e,t,{trailers:n=[]}={}){let{stdout:r}=await je(e,["status","--porcelain"]);return r.trim()?(await je(e,["add","-A"]),await je(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function oi(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await je(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 si(e,t){if(!t)return 0;try{let{stdout:n}=await je(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function gs(e,t,n,r){return await je(e,["worktree","add","--no-track","-b",n,t,r]),t}async function k0(e,t){try{return await je(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function El(e,t,n){return await je(e,["worktree","add",t,n]),t}async function ys(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await je(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 je(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Sp(e){await je(e,["init","-q"]),await je(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function vt(e,t){try{await je(e,["worktree","remove","--force",t])}catch{try{await je(e,["worktree","prune"])}catch{}}}function BN(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 Tp(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Ap(e,t,n,r){let{stdout:o}=await je(e,Tp(["diff","--shortstat",t,n],r));return BN(o)}async function Ep(e,t,n,r){let{stdout:o}=await je(e,Tp(["diff","--name-only",t,n],r));return o.split(`
|
|
173
|
+
`).map(s=>s.trim()).filter(Boolean)}async function _p(e,t,n,r){let{stdout:o}=await je(e,Tp(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await kp(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await kp(e,o,["--3way","--whitespace=nowarn"]),a=await jN(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await kp(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function kp(e,t,n){try{return await je(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function jN(e){try{let{stdout:t}=await je(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
174
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var xp,y0,_l=j(()=>{xp=PN(NN),y0=64*1024*1024});function Il(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!LN.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 qe,LN,Cn=j(()=>{qe=e=>!!(e&&(e.archivedAt||e.transferredTo)),LN=new Set(["merged","conflict","canceled","failed","handed-off"])});import Ip from"node:fs/promises";import v0 from"node:path";import FN from"node:os";function E0(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function _0(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function I0(e){let t=new Date(E0(e));return t.setDate(t.getDate()+7),t.getTime()}function $0(e){let t=new Date(_0(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Rl(e=Date.now()){for(let[t,n]of Object.entries(bs)){let r=n.startOf(e);if(r!==Mn[t]){Mn[t]=r;for(let o of lo.values())o[t]=0;for(let o of ks)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&ks.delete(o)}}}function R0(e,t){let n=Gt?.caps?.[e]?.[bs[t].capKey];return Number.isFinite(n)&&n>0?n:null}function uo(e,t=Date.now()){if(!e||!Rp||!Gt?.caps)return null;Rl(t);let n=lo.get(e);for(let r of Object.keys(bs)){let o=R0(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:$l(s),resetsAt:bs[r].nextStart(t)}}return null}function O0(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${Mn[t.window]}`;return ks.has(n)?!1:(ks.add(n),!0)}function x0(e,t,n,r=Date.now()){if(!e||!t)return;Rl(r);let o=UN(e);for(let[s,a]of Object.entries(bs))n>=Mn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function HN(e,t,n=Date.now()){if(!Gt?.caps||!$p)return;let r=lo.get(e);if(r)for(let[o,s]of Object.entries(bs)){let a=R0(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*A0||i>=a)continue;let c=`warn:${e}:${o}:${Mn[o]}`;if(!ks.has(c)){ks.add(c);try{let l=await $p(t);if(!l)continue;await O(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:$l(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function C0(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 Ol(e,{source:t="local",updatedAt:n=Date.now()}={}){Gt={caps:C0(e),updatedAt:n,source:t};try{await Ip.mkdir(S0,{recursive:!0}),await Ip.writeFile(T0(),JSON.stringify(Gt,null,2))}catch{}return ai()}function ai(e=Date.now()){Rl(e);let t=new Set([...lo.keys(),...Object.keys(Gt?.caps||{})]),n={};for(let r of t){let o=lo.get(r);n[r]={weeklyUsd:$l(o?.weekly||0),monthlyUsd:$l(o?.monthly||0)}}return{caps:Gt?.caps||{},configured:!!Gt,seeded:Rp,updatedAt:Gt?.updatedAt??null,spent:n,windows:{weekly:{start:Mn.weekly,resetsAt:I0(e)},monthly:{start:Mn.monthly,resetsAt:$0(e)}},warnPct:A0}}async function M0({listTracks:e,getTrack:t}={}){$p=t||null,Rl();try{let o=JSON.parse(await Ip.readFile(T0(),"utf8"));Gt={caps:C0(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{Gt=null}let n=Date.now();ht.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ot(o);x0(o.provider||"unknown",s,o.ts??Date.now()),HN(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Mn.weekly,Mn.monthly);for(let o of await e?.()||[]){let s;try{s=await F(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||x0(a.provider||"unknown",Ot(a).usd,i,n)}}return Rp=!0,ai()}var S0,T0,A0,bs,Gt,Rp,Mn,lo,ks,$p,UN,$l,Cl=j(()=>{ue();gr();S0=process.env.BOSTRAT_STATE_DIR||v0.join(process.env.HOME||FN.tmpdir(),".bostrat"),T0=()=>v0.join(S0,"usage-caps.json"),A0=.8;bs={weekly:{startOf:E0,nextStart:I0,capKey:"weeklyUsd"},monthly:{startOf:_0,nextStart:$0,capKey:"monthlyUsd"}},Gt=null,Rp=!1,Mn={weekly:0,monthly:0},lo=new Map,ks=new Set,$p=null,UN=e=>{let t=lo.get(e);return t||(t={weekly:0,monthly:0},lo.set(e,t)),t};$l=e=>Math.round(e*1e4)/1e4});import WN from"node:fs/promises";import qN from"node:os";import N0 from"node:path";function U0(e){return e==="codex"?zN:e==="cursor"?F0:j0}function P0(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 D0(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 JN(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 B0(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 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 Ct(e,s)}catch(l){throw new Error(ln(l))}finally{un()}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 Gx(e,t){let n=await jn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Ct(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(ln(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Kp(i))throw i;let c=await Ct(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Bx(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(ln(i));o=jx(l).map(Px).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=FP(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Ct(n.path,["run","view",c,"--log-failed"]).then(u=>UP(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function FP(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function UP(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
185
|
`+r.slice(-n)),r}function ln(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
186
|
-
`).find(n=>n.trim())||"gh failed"}var Es,
|
|
187
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+iD(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 ov(e){let t=nv(e),n=t.match(oD);if(!n)return null;let r=Xp(t,n.index);return aD.test(r)?null:{repo:r.match(rv)?.[1]??null,head:r.match(cD)?.[1]??null}}function sv(e){let t=nv(e),n=t.match(sD);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("-")){uD.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(rv)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function fD(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Bn("observer");let{stdout:c}=await Zx("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",dD],{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-tD&&!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 pD(e,t){if(t)return[t];let n=[];for(let r of av(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 av(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 mD(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)||lD(c.headRefName,e.id),i=[];for(let c of await pD(e,t?.repo??null)){let l;try{l=await fD(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 iv(e,t,{delays:n=Qx,turnEnd:r}={}){let o=ov(t.command);if(!o)return null;let s=!!sv(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 mD(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 cv(e,t,{delays:n=Qx,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(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=av(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(T=>setTimeout(T,m));let w=[];for(let T of d)try{Bn("observer");let{stdout:A}=await Zx("gh",["pr","view",String(l),"--repo",T,"--json",hD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await tn({slug:T})}}),R=JSON.parse(A||"null");R?.number&&w.push({slug:T,pr:R})}catch(A){Tr(A)&&Ar(A)}let y=w.find(T=>T.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 S=await Qe(c,x.number);if(S&&S.trackId!==e.id)return null;S||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(T=>T.type==="pr.state.changed"&&T.payload?.change==="merged"&&T.payload?.number===x.number))return null;let $=T=>T.some(A=>A.type==="pr.state.changed"&&A.payload?.change==="opened"&&A.payload?.number===x.number),D=0;if(!$(I)){if(ov(t.command)&&await Nn(c,x.number)){let T=Date.now();D=T+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:T,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 T=o?.capMs??nD,A=o?.pollMs??rD,R=Date.now()+T;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 Zx,Qx,QP,eD,tD,nD,rD,ev,tv,oD,sD,aD,Xx,iD,rv,cD,lD,uD,dD,Er,hD,lv=j(()=>{ue();tr();it();di();Pn();_r();gi();Cs();Zx=ZP(XP),Qx=[4e3,15e3,45e3],QP=2500,eD=5*6e4;tD=15e3,nD=9e4,rD=2e3,ev=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,tv=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,oD=new RegExp(`${ev}${tv}create\\b`),sD=new RegExp(`${ev}${tv}merge\\b`),aD=/\s--web\b/,Xx=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,iD=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");rv=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,cD=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,lD=Yx;uD=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);dD="number,url,title,headRefName,createdAt";Er=new Set;hD="number,url,title,headRefName,state"});import wD from"node:fs/promises";import Qp from"node:path";import gD 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=yD.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Zp(n,"from"),s=o?o.replace(/^uds:/,""):null,a=uv(s);return{socket:s,pid:a,name:Zp(n,"from-name"),mode:Zp(n,"from-mode"),text:r.trim()}}function uv(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function bD(){return process.env.CLAUDE_CONFIG_DIR?Qp.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Qp.join(gD.homedir(),".claude","sessions")}async function kD(e){let t=uv(e);if(!t)return null;try{let n=await wD.readFile(Qp.join(bD(),`${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 dv(e,t){let n=await kD(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 yD,em=j(()=>{yD=/<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(),fv);try{let a=await pv(`${$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 mv(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<xD)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(),fv);try{let r=await pv(`${$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 fv,pv,xD,Ql,rm=j(()=>{it();fv=1e4,pv=(...e)=>fetch(...e);xD=10*6e4,Ql=new Map});import{randomUUID as vD}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 hv(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function wv(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(hv({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(wv({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 gv(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 dv(a,ki),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=Zy(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}}),mv(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=Q0(o.text);if(c){let l=nx(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 Kx(e,{branch:d,rebase:l}).catch(()=>null),p=f?null:tx(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:vD(),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 yv(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=hv(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}}),iv(e,o),cv(e,o))}}catch{}}async function bv(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=wv(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,kv=j(()=>{ue();Ss();Gx();lv();Cs();em();_r();rl();rm();om=new Map,eu=new Map});function SD(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
188
|
-
`)){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
|
|
189
|
-
`)){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===
|
|
190
|
-
`)){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
|
|
191
|
-
`)){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
|
|
186
|
+
`).find(n=>n.trim())||"gh failed"}var Es,_P,Vl,qp,Dx,$P,As,Nx,OP,CP,fi,pi,NP,PP,DP,Hp,$s=j(()=>{tr();it();Pn();di();Gl();ue();Es=AP(TP),_P={timeout:3e4,maxBuffer:8*1024*1024};Vl=null;qp="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Dx=qp.replace("statusCheckRollup,","");$P=9e4,As=new Map;Nx=(e,t)=>e.number-t.number;OP=3e4,CP=1500,fi=new Map;pi=new Set(["squash","merge","rebase"]);NP=2500,PP=9e4,DP=e=>new Promise(t=>setTimeout(t,e));Hp=new Map});import{execFile as HP}from"node:child_process";import{promisify as WP}from"node:util";async function KP(e,t){try{let{stdout:n}=await qP("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function GP(e,t,n,{rebase:r=!1,listPrs:o=cn}={}){let s=await oi(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await si(e.dir,n).catch(()=>0),i=t?await KP(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let d=s||a>0,f=[];s&&f.push("commit"),r&&f.push("rebase"),(s||c)&&f.push("push"),d&&(u!=="open"||s||c)&&f.push("pr");let p=f.filter(m=>!(m==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:Jl.filter(m=>f.includes(m)),defaults:Jl.filter(m=>p.includes(m))}}async function Vx(e,{branch:t,rebase:n=!1,listPrs:r=cn}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(f=>f?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await hs(s[0].dir).catch(()=>!1))return null;let a=[];for(let f of s)a.push(await GP(f,t,f.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(f=>f.dirty||f.aheadBase>0||f.openPr),c=i.length?i:a,l=Jl.filter(f=>c.some(p=>p.available.includes(f))),u=Jl.filter(f=>c.some(p=>p.defaults.includes(f))),d=c.filter(f=>f.openPr);return{available:l,defaults:u,prState:d.length?"open":c.every(f=>f.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(f=>({name:f.name,slug:f.slug,dirty:f.dirty,unpushed:f.unpushed,prState:f.prState,openPr:f.openPr}))}}var qP,Jl,zx=j(()=>{_l();$s();qP=WP(HP),Jl=["commit","rebase","push","pr"]});function Yl(e,t,n){let{state:r,reason:o}=Tf(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of wi)a-c.at>=Jx()&&wi.delete(i);wi.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=wi.get(e);return s?Date.now()-s.at>=Jx()?(wi.delete(e),!1):s.busy:!1}async function Jp(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Oe(e,{lines:ze}).catch(()=>"");if(!Yl(e,t,r))return bb(t,r)?{working:!0,prompted:!1}:n;if(co(t,r)){let o=await $n(e).catch(()=>r),s=br({name:e,agent:t},o);if(s)return!Al({name:e,agent:t},s,o)||w0(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function wo(e,t){return(await Jp(e,t)).working}var Jx,wi,gi=j(()=>{nt();Ga();gp();bp();Jx=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,wi=new Map});import{execFile as VP}from"node:child_process";import{promisify as zP}from"node:util";async function Rs(e){if(!e)return null;let t=Yx.get(e);if(t&&Date.now()-t.at<YP)return t.branch;let n=null;try{let{stdout:r}=await JP("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return Yx.set(e,{at:Date.now(),branch:n}),n}function Xx(e){return String(e||"").match(XP)?.[1]??null}function ZP(e,t){let n=Xx(e);return!!n&&!!t&&n===t}function Zx(e,t){let n=Xx(e);return!!n&&n!==t}function QP(e){let t=e?.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.dir).filter(Boolean):[t?.dir||e?.cwd].filter(Boolean)}async function Os(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of QP(e))n(await Rs(r));return t}async function Xl(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=>ZP(t,a.id));if(s)return s;for(let a of r)if(!qe(a)&&(await Os(a)).includes(t))return a;return null}var JP,YP,Yx,XP,Cs=j(()=>{Cn();JP=zP(VP),YP=5e3,Yx=new Map;XP=/^track\/.+-([0-9a-f]{8})$/});import{execFile as eD}from"node:child_process";import{promisify as tD}from"node:util";async function Yp(e,t,{midTurn:n=wo,pollMs:r=nD,capMs:o=rD}={}){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 ov(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){Qx.lastIndex=n;let o=Qx.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*":"")+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
|
+
`)){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 _D(e,t){let n=`${e.id}:${t.name}`;if(!am.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);am.set(n,o)}return am.get(n)}async function Sv(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Zr(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await _D(e,t),o=ED(await gt(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 O(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:an(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await O(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 am,Tv=j(()=>{ue();eo();qt();On();am=new Map});import tu from"node:fs";import cm from"node:path";import ID from"node:os";async function OD({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(RD,{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:Av})});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<Av)break}return r}function CD(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 _v(){try{return JSON.parse(tu.readFileSync(nu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function ND(e){try{tu.mkdirSync(cm.dirname(nu()),{recursive:!0});let t=nu()+".tmp";tu.writeFileSync(t,JSON.stringify(e)),tu.renameSync(t,nu())}catch{}}async function DD({fetchImpl:e,now:t=Date.now()}={}){if(Ir.rows&&t-Ir.at<MD)return Ir.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=_v(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await OD({sinceMs:o,cookie:n,fetchImpl:e});Ir={at:t,rows:s.map(CD).filter(Boolean),error:null}}catch(s){Ir={at:t,rows:null,error:String(s?.message||s)}}return Ir.rows}async function BD(e,t){let n=`${e.id}:${t.name}`;if(!im.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);im.set(n,o)}return im.get(n)}async function Iv(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!Ev())return;let r=await DD({fetchImpl:n});if(!r||!r.length)return;let o=await zk(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await BD(e,t),a=_v(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=an(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await O(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}ND({sinceMs:c,seen:[...i].slice(-PD)})}catch{}}var $D,Ev,RD,Av,MD,Ir,nu,PD,lm,im,um=j(()=>{ue();rp();On();$D=process.env.BOSTRAT_STATE_DIR||cm.join(process.env.HOME||ID.tmpdir(),".bostrat"),Ev=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,RD="https://cursor.com/api/dashboard/get-filtered-usage-events",Av=100;MD=5*6e4,Ir={at:0,rows:null,error:null},nu=()=>cm.join($D,"cursor-usage.json");PD=5e3;lm=()=>({enabled:Ev(),lastPollAt:Ir.at||null,error:Ir.error}),im=new Map});import{execFile as jD}from"node:child_process";import{promisify as LD}from"node:util";function Ns({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function ru(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function ou({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?qD[r]:null;return a&&s.push(a),s.push(`${Cv}: ${Ns({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${Mv}: ${String(o).trim()}`),s}function $v(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
189
|
+
`)){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===Cv.toLowerCase()&&!t.epoch)t.epoch=ru(a);else if(s===Mv.toLowerCase()&&!t.model)t.model=a;else if(s===Nv.toLowerCase()&&!t.cause){let i=a.toLowerCase();xi.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 KD(e,t=$r){return e<=t?"rework":"drift"}function GD(e,{window:t=$r,end:n=mm}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function YD({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return VD.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${Nv} trailer`}:JD.test(s)&&!Rv.test(s)?{cause:"style",level:"C",basis:"subject"}:Rv.test(s)?{cause:"defect",level:"C",basis:"subject"}:zD.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 ZD(e){return XD.some(t=>t.test(e))}function QD(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
190
|
+
`)){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 e1(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
191
|
+
`)){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 Ov(e,t){let{stdout:n}=await pm(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
192
192
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
193
|
-
`)}}async function Nv(e,t,{fixKey:n=null,window:r=$r,resolveOrigin:o=null,context:s={}}={}){let a=await $v(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=YD(d),p=0,m=0,w=0,y=new Map;for(let D of f){if(D.binary)continue;let T=D.oldPath||D.newPath||"",A=D.deleted.reduce((E,[,L])=>E+L,0);if(JD(T)){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=XD((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=[],S=0,I=new Map;for(let[D,{lines:T,time:A}]of y){let R=I.get(D);if(!R){try{R=await $v(e,D)}catch{R=null}I.set(D,R)}let E=R?_v(R.message):{epoch:null},L=E.epoch,U=E.provider,oe=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,oe=oe??te.model??null)}if(!L){S+=T;continue}let G=Number.isFinite(A)?A:R?.ts??a.ts,C=Math.max(0,(a.ts-G)/LD),Z=Ns(L),Y=Z===u?"self":HD(C,r);x.push({sha:D,key:Z,trackId:L.trackId,boxId:L.boxId,epoch:L.epoch,provider:U??null,model:oe??null,lines:T,ageDays:Math.round(C*100)/100,class:Y,decay:Y==="rework"?1:Y==="drift"?Math.round(WD(C,{window:r})*1e3)/1e3:0})}x.sort((D,T)=>T.lines-D.lines);let $=VD({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:S,ghost:m===0&&p>0,heuristic:$}}function Pv(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 Dv(e,{window:t=$r,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,A,R)=>Ns({trackId:T,boxId:A,epoch:R??0}),d=(T,A={})=>{if(!l[T]){let E=ru(T)||{};l[T]={key:T,...E,provider:null,model:null,...dm()}}let R=l[T];return A.provider&&!R.provider&&(R.provider=A.provider),A.model&&!R.model&&(R.model=A.model),R};for(let T of o){if(!T.boxId)continue;let A=u(T.trackId,T.boxId,T.epoch),{usd:R,model:E}=Ot(T),L=d(A,{provider:T.provider,model:E!=="unknown"?E:null});L.genUsd+=R}let f=new Map;for(let T of s){let A=`${T.trackId}/${T.boxId}`;(f.get(A)??f.set(A,[]).get(A)).push(T)}for(let T of f.values())T.sort((A,R)=>A.ts-R.ts);let p=(T,A,R,E)=>{let L=f.get(`${T}/${A}`)||[],U=-1/0;for(let C of L)C.ts<E&&(U=C.ts);let oe=0,G=0;for(let C of o)C.trackId!==T||C.boxId!==A||(C.epoch??0)===(R??0)&&(C.ts<=U||C.ts>E||(oe+=Ot(C).usd,G++));return{usd:oe,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=[],S={},I={},$=new Set;for(let T of a){let A=T.payload||{};if(!A.sha||$.has(A.sha))continue;$.add(A.sha);let R=ru(A.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:E,turns:L}=p(R.trackId,R.boxId,R.epoch,A.ts??T.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 oe=U?.level??"none";w[oe]=(w[oe]||0)+1;let G=FD.has(oe),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??T.ts,trackId:T.trackId,fixKey:A.fixKey??null,fixProvider:A.fixProvider??T.provider??null,fixUsd:Vt(E),fixTurns:L,linesReworked:Y,unattributedLines:Number(A.unattributedLines)||0,ghost:A.ghost===!0,verdict:U?{cause:U.cause,level:oe,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 T of Object.values(l)){let A=T.provider||"unknown",R=T.model||"unknown",E=L=>{L.genUsd+=T.genUsd,L.reworkChargedUsd+=T.reworkChargedUsd,L.reworkPendingUsd+=T.reworkPendingUsd,L.linesReworked+=T.linesReworked,L.fixes+=T.fixes;for(let U of Object.keys(L.causes))L.causes[U]+=T.causes[U]};E(S[A]||=dm()),E(I[R]||=dm())}y.sort((T,A)=>A.ts-T.ts);let D=Object.values(l).map(T=>{let A=n[`${T.trackId}/${T.boxId}`]||{};return fm({...T,trackName:A.trackName??null,label:A.label??null})}).sort((T,A)=>A.reworkChargedUsd+A.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||A.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(D.map(T=>[T.key,T])),epochs:D,byProvider:Object.fromEntries(Object.entries(S).map(([T,A])=>[T,fm(A)])),byModel:Object.fromEntries(Object.entries(I).map(([T,A])=>[T,fm(A)])),buckets:Object.fromEntries(Object.entries(m).map(([T,A])=>[T,Vt(A)])),fixUsdTotal:Vt(Object.values(m).reduce((T,A)=>T+A,0)),fixes:y,verdicts:w,pendingTaps:x,commits:s.length}}var BD,jD,pm,Rv,Ov,Cv,LD,$r,mm,Mv,xi,FD,UD,qD,Iv,KD,GD,zD,Vt,dm,su=j(()=>{gr();BD=DD(PD),jD=64*1024*1024,pm=(e,t)=>BD("git",["-C",e,...t],{maxBuffer:jD,timeout:6e4}),Rv="Bostrat-Epoch",Ov="Bostrat-Model",Cv="Bostrat-Fix-Cause",LD=24*36e5,$r=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,mm=90,Mv=3,xi=Object.freeze(["defect","design","requirements","style"]),FD=new Set(["A","B","E"]);UD={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};qD=/^revert\b|^revert "/i,Iv=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,KD=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,GD=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;zD=[/(^|\/)\.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 ZD from"node:os";import QD from"node:net";import{randomUUID as e1}from"node:crypto";import{execFile as t1}from"node:child_process";import{promisify as n1}from"node:util";function l1(e){for(let t of xm(e))if(!Hv.test(t))return t;return null}function Wv(e){let t=xm(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function u1(e,t){let n=Wv(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 f1(e){let t=!1,n=!1;for(let r of xm(e)){if(!t){if(Hv.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(d1(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function m1(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 h1(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 qv(yt.join(e,o.name,ym))?`./${o.name}`:null))).filter(Boolean)}async function w1(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 g1(e,t){let n=f1(t);if(n)throw vi(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await p1(e))throw vi(`serve directory does not exist: ${e}`);let r=l1(t);if(!r)return;if(!r.includes("/")&&!await Ey(r,{loginShell:!0,failOpen:!0}))throw vi(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(a1.test(t)||i1.has(r)||!s1.has(r)||!Wv(t).includes("run")||c1.test(t))return;let o=u1(e,t);if(await qv(yt.join(o,ym)))return;let s=await h1(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(Uv,"utf8"))}catch{dn={views:[],adoptWatermarks:{}}}finally{wm=null}return dn.views||=[],dn.adoptWatermarks||={},dn})(),wm)}async function pn(){await wt(Uv,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 Gv(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 Vv(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 y1(e,t){let n=km(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!Lv.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Lv.has(s))return s}}function Ai(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=QD.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 zv(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function b1(e,t){let n=new Set(e.views.map(o=>o.port)),r=zv();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=Bv;o<=jv;o++)if(!(n.has(o)||r.has(o))&&!await Ai(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Bv}-${jv}`)}async function k1(e){try{let{stdout:t}=await fn(W,q("list-sessions","-F","#{session_name}"));return t.split(`
|
|
194
|
-
`).some(n=>n.trim()===Ps(e))}catch{return!1}}async function Ei(e){try{await fn(W,q("kill-session","-t",Ps(e)))}catch{}}async function Am(e){if(!await Da())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Ps(e.slug),n=e.serveCwd??e.cwd,r=yt.dirname(process.execPath);await fn(W,q("new-session","-d","-s",t,"-c",n)),await fn(W,q("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await fn(W,q("send-keys","-t",t,"Enter")),await fn(W,q("send-keys","-t",t,"-l",e.cmd)),await fn(W,q("send-keys","-t",t,"Enter"))}async function
|
|
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()===Ps(e))}catch{return!1}}async function Ei(e){try{await fn(W,q("kill-session","-t",Ps(e)))}catch{}}async function Am(e){if(!await Da())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Ps(e.slug),n=e.serveCwd??e.cwd,r=yt.dirname(process.execPath);await fn(W,q("new-session","-d","-s",t,"-c",n)),await fn(W,q("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await fn(W,q("send-keys","-t",t,"Enter")),await fn(W,q("send-keys","-t",t,"-l",e.cmd)),await fn(W,q("send-keys","-t",t,"Enter"))}async function Hv(e,t=12){try{let{stdout:n}=await fn(W,q("capture-pane","-t",Ps(e),"-p"));return String(n||"").trimEnd().split(`
|
|
195
195
|
`).filter(Boolean).slice(-t).join(`
|
|
196
|
-
`)}catch{return""}}async function v1(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await k1(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await Fv(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>x1?(e.state="error",e.error=await Fv(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 v1(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 Jv(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 w1(r,u),await g1(u,n)},f=await Wn();if(i!=="detected"){let w=km(t),y=String(n).trim(),x=f.views.find(S=>S.trackId===e&&(S.slug===w||km(S.name)===w||S.cmd===y&&yt.resolve(S.serveCwd??S.cwd)===u));if(x)return S1(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:e1().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:y1(f,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await b1(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 S1(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 m1(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 Yv(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 Xv(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 Zv(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 Qv(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 eS(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(T1)){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(!zv().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,r1,Uv,Bv,jv,Rr,o1,Ps,Lv,vi,s1,ym,a1,i1,c1,xm,Hv,hm,d1,p1,qv,au,dn,wm,Si,vm,Kv,Un,x1,gm,T1,uu=j(()=>{nt();ue();rr();fn=n1(t1),r1=process.env.BOSTRAT_STATE_DIR||yt.join(process.env.HOME||ZD.tmpdir(),".bostrat"),Uv=yt.join(r1,"views.json"),[Bv,jv]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Rr=Number(process.env.BOSTRAT_VIEW_MAX)||3,o1="bostrat-view-",Ps=e=>`${o1}${e}`,Lv=new Set(["bostrat","www","api","views","app"]),vi=e=>Object.assign(new Error(e),{status:400}),s1=new Set(["npm","pnpm","yarn","bun"]),ym="package.json",a1=/[|;&`]|\$\(/,i1=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),c1=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,xm=e=>String(e).trim().split(/\s+/).filter(Boolean),Hv=/^[A-Za-z_][A-Za-z0-9_]*=/;hm=e=>e==="/",d1=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");p1=e=>Ti.stat(e).then(t=>t.isDirectory(),()=>!1),qv=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,Kv=12e3;Un=new Map,x1=12e4;gm=!1;T1=/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>E1?null:n}function _1(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function tS(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)<A1)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(!_1(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 A1,Im,E1,$m=j(()=>{ue();$s();tr();Pn();gi();Cs();A1=6e3,Im=new Map,E1=30*6e4});import Rm from"node:os";import{execFile as I1}from"node:child_process";import{promisify as $1}from"node:util";async function fu(){if(process.platform!=="darwin")return Rm.freemem();try{let{stdout:e}=await R1("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 R1,Om=j(()=>{R1=$1(I1)});import We from"node:fs/promises";import Ke from"node:path";import sS from"node:os";import{randomUUID as O1}from"node:crypto";import{execFile as C1}from"node:child_process";import{promisify as M1}from"node:util";import{chromium as N1}from"playwright-core";async function wn(){return go||(Cm||=(async()=>{try{go=JSON.parse(await We.readFile(iS,"utf8"))}catch{go={mocks:[]}}finally{Cm=null}return go.mocks||=[],go})(),Cm)}async function Mt(){await wt(iS,go)}function dS(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(U1)}function H1(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 W1(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 q1({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 K1(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 G1(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 V1({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await N1.launch({executablePath:cS,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 K1(a,i),u=l&&q1({...l,pad:B1*Ds,align:Ds});u&&(i=await G1(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<lS)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 oS({htmlPath:e,outPath:t,width:n}){let r=await We.mkdtemp(Ke.join(sS.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Nm(nS,["--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 (${nS})`,""):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<lS)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 W1(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 z1(e){if(P1==="firefox")return oS(e);try{return await V1(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await oS(e)}catch(n){throw/not found/.test(n?.message||"")?new Tt(`no browser installed \u2014 chromium missing at ${cS} 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 fS(e){return Mm=Mm.then(e,e),Mm}async function pS(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<j1)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 J1({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 Y1({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?D1: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 mS({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=H1(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>rS)throw new Error(`video must be ${rS/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function hS({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=Y1({trackId:e,name:t,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=mS({html:n,image:r,video:o}),m=await wn(),w={id:O1().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"),fS(()=>pS(w.id,1))):o?(await gS(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),dS(w,w.versions[0])):(await yS(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 wS(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=mS({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"),fS(()=>pS(a.id,l))):r?(await gS(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),dS(a,a.versions.find(u=>u.v===l))):(await yS(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 X1(e){try{let{stdout:t}=await Nm(L1,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:uS,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 Z1(e,t){try{await Nm(F1,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:uS,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await We.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function gS(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 X1(s),c=await Z1(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 yS(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 bS(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 kS(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 xS(e){let n=(await wn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Ke.join(aS,"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 vS(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 SS(){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,aS,iS,P1,cS,nS,D1,Ds,B1,lS,Bs,j1,rS,L1,F1,uS,Tt,go,Cm,mn,hn,Ii,U1,J1,Mm,js=j(()=>{ue();rr();Om();sr();Nm=M1(C1),aS=process.env.BOSTRAT_STATE_DIR||Ke.join(process.env.HOME||sS.tmpdir(),".bostrat"),iS=Ke.join(aS,"mocks.json"),P1=process.env.BOSTRAT_MOCK_ENGINE||"chromium",cS=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",nS=process.env.BOSTRAT_FIREFOX_BIN||"firefox",D1=1300,Ds=2,B1=32,lS=1500,Bs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,j1=700*1024*1024,rS=100*1024*1024,L1=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",F1=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",uS=2e4,Tt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
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}
|
|
197
197
|
${n.trim().split(`
|
|
198
198
|
`).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");U1=Buffer.from([137,80,78,71,13,10,26,10]);J1=z1,Mm=Promise.resolve()});import Q1 from"node:fs/promises";import IS from"node:path";import eB from"node:os";import{randomUUID as $S}from"node:crypto";async function Gn(){return Kn||(Bm||=(async()=>{let e=!0;try{Kn=JSON.parse(await Q1.readFile(RS,"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(RS,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 OS({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>TS)throw new Error(`steps must be an array of 1-${TS}`);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>ES)throw new Error(`${l}: caption over ${ES} 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(S=>S.id===f);if(p.length||(p=r.filter(S=>S.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(S=>`mock:${S.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(S=>S.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(S=>S.mockId===m.id),x=y>=0?o.splice(y,1)[0].id:$S().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 nB(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>AS)throw new Error(`name over ${AS} chars`);return e.trim()}async function CS({trackId:e}={}){let t=await Gn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function rB(e){let n=(await Gn()).flows.find(r=>r.id===e);return n?{...n}:null}async function MS(e){let t=await rB(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 NS({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=nB(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 OS({trackId:e,steps:n}),d=Date.now(),f={id:$S().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 PS(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 OS({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 DS(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 BS(e){return(await Gn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function jS(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 LS(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 FS(){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 tB,RS,TS,AS,ES,_S,Kn,Bm,mu,Lm=j(()=>{ue();rr();js();sr();tB=process.env.BOSTRAT_STATE_DIR||IS.join(process.env.HOME||eB.tmpdir(),".bostrat"),RS=IS.join(tB,"flows.json"),TS=20,AS=80,ES=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 oB from"node:os";import{randomUUID as sB}from"node:crypto";function Ri(e){return aB.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(HS,"utf8"))}catch{Vn={runbooks:[]},e=!1}finally{Fm=null}return Vn.runbooks||=[],!e&&Me()&&await iB().catch(()=>{}),Vn})(),Fm)}async function bo(){await wt(HS,Vn)}function wu(e){return Oi(ct("runbooks",{...e},{trackId:e.trackId}))}async function iB(){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 WS(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 qS(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>US)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${US/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 cB(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function lB({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 KS({trackId:e,status:t}={}){let n=await gn(),r=await lB({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 uB(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 GS(e,t){let n=await $a("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function uB(e){let n=(await gn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function VS({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=qS(r),c=await gn(),l={id:sB().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 zS(e,{md:t,note:n}={}){let o=(await gn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");cB(o,"update");let s=qS(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 JS(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 YS(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 XS(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 ZS(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 QS(){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,HS,hu,US,aB,Vn,Fm,Oi,gu=j(()=>{rr();ue();sr();yo=process.env.BOSTRAT_STATE_DIR||Yn.join(process.env.HOME||oB.tmpdir(),".bostrat"),HS=Yn.join(yo,"runbooks.json"),hu=Yn.join(yo,"runbooks"),US=256*1024,aB=[{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 dB}from"node:crypto";function qm(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return fB.has(t)?"image":pB.has(t)?"doc":null}function mB(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 nT(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>eT)throw new Error(`file too large \u2014 max ${Math.floor(eT/(1024*1024))} MB`);let r=mB(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=dB().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:`${tT}/${c}`,kind:s,size:n.length}}function rT(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"&&hB.test((e.text||"").trim())}var tT,Wm,eT,fB,pB,hB,Gm=j(()=>{tT=".bostrat/uploads",Wm=e=>Ci.join(e,tT),eT=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,fB=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),pB=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"]);hB=/\[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 oT=j(()=>{});import{execFile as wB}from"node:child_process";import{promisify as gB}from"node:util";function Vm(e){return`refs/bostrat/wip/${e}`}function kB(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function iT(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function xB(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:bB}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 sT.has(o)||(sT.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}`],aT),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 Jm(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Or(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Or(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await ws(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await Or(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(yu.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=
|
|
201
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
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 Jm(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Or(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Or(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await ws(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await Or(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(yu.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=SB(n);return await Or(r,["push","origin",`${o}:${i}`],cT),yu.set(r,a),await O(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(`
|
|
201
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function uT({listTracks:e,sweepMs:t=TB(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)qe(i)||i?.kind==="setup"||await zm(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 xB,vB,cT,Or,yu,iT,Ym=j(()=>{_l();ue();Cn();xB=kB(bB),vB=12e4,cT=6e4;Or=(e,t,n=15e3)=>xB("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),yu=new Map,iT=new Set});import AB from"node:fs";import Xm from"node:fs/promises";import EB from"node:path";function RB(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function dT(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(!RB(t))return{restored:!1,reason:"plane not configured"};let s=Ko(e),a=EB.join(s,`${o}.ndjson`);try{if(AB.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 f=new AbortController,p=setTimeout(()=>f.abort(),$B),m;try{m=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${_B}`,{headers:c,signal:f.signal})}finally{clearTimeout(p)}if(m.status===404)return{restored:!1,reason:"unknown to plane"};if(!m.ok)return{restored:!1,reason:`plane ${m.status}`};let w=await m.json();for(let y of w.events||[])l.push(JSON.stringify(y));if(l.length>IB)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 Xm.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await Xm.writeFile(d,l.join(`
|
|
202
202
|
`)+`
|
|
203
|
-
`),await Xm.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var
|
|
203
|
+
`),await Xm.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var _B,IB,$B,fT=j(()=>{it();ue();_B=1e3,IB=5e5,$B=3e4});var KT={};Ad(KT,{HANDOFF_LATEST_REL:()=>xo,KNOWN_AGENTS:()=>Wi,TRACK_NAME_MAX:()=>uh,addTrackRepo:()=>Eh,adoptTrack:()=>qh,agentAvailability:()=>vu,announceNodeUpdate:()=>bn,answerFullscreenOffer:()=>Bh,answerPrompt:()=>Tu,archiveTrack:()=>Ao,archiveTrackForMergedPr:()=>Ln,augmentFirstPromptAfterHandoff:()=>vT,buildAgentCmd:()=>ph,canResumeSubtask:()=>HT,cancelQueued:()=>Gi,cancelQueuedToDraft:()=>bh,cancelStopJob:()=>Vi,cancelSubtask:()=>Mu,changedRepos:()=>Ms,commitTransferOut:()=>Ou,createSetupTrack:()=>$h,createTrack:()=>_u,deleteTrack:()=>Oh,demoteTransferredTracks:()=>Wh,ensureLeadEpochs:()=>Gs,forceMergeSubtask:()=>zh,getHandoffJob:()=>Ys,getSubtaskJob:()=>Cj,getTrack:()=>oe,getTransferOutJob:()=>Fh,healDeadLead:()=>Ui,interruptLead:()=>Eu,isArchiving:()=>vj,isHandoffNoteEcho:()=>ah,isInterruptEcho:()=>ih,isModelSwitchEcho:()=>ch,leadCliDead:()=>Ch,listSubtaskJobs:()=>Eo,listTracks:()=>ie,matchTranscriptsToBoxes:()=>AT,mergeNotifyMessage:()=>jT,navigatePrompt:()=>yh,notifyTrackForMergedPr:()=>Fn,notifyTrackForResolvedPr:()=>Iu,pendingOffers:()=>wh,queueShipCompleteNotify:()=>xh,queuedMessages:()=>vo,reapArchivedOrphans:()=>Dh,reconcileDemotedTracks:()=>Ph,reconcileHandoffOutcome:()=>LT,reconcileInterruptedHandoffs:()=>Lh,reconcileInterruptedSubtasks:()=>Kh,reconcileNodeUpdateNotices:()=>hh,reconcileSubtaskOutcome:()=>UT,refreshStandingInstructions:()=>Ih,refuseIfTransferred:()=>Hh,rehydrateLead:()=>Ji,removeTrackRepo:()=>_h,renameTrack:()=>Nh,reposOf:()=>Et,resolveTrackBox:()=>ki,resolveTrackBoxByHookToken:()=>Ki,restartLead:()=>$u,resumeAgentCmd:()=>mh,sayToLead:()=>Bt,sendOptionsForAgent:()=>sn,setTrackDraft:()=>Th,setTrackModel:()=>Ah,startLeadHandoff:()=>Ru,startSpinUpView:()=>vh,startSubtask:()=>Vh,startTransferOut:()=>Uh,stopJobActive:()=>IT,subtaskMessages:()=>gh,trackMessages:()=>Su,unarchiveTrack:()=>Mh});import K from"node:fs/promises";import J from"node:path";import{randomUUID as To}from"node:crypto";import{execFile as OB}from"node:child_process";import{promisify as CB}from"node:util";function ph(e,{skipPermissions:t=!0}={}){let n=xu[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 mh(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=xu[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&&!Wt(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 Js(e){let t=xu[e];if(t)return process.env[t.envVar]||t.base}async function vu({maxAgeMs:e=6e4}={}){if(Cr.value&&Date.now()-Cr.at<e)return Cr.value;let t=Cr.value,n=await Promise.all(Wi.map(async r=>{let o=await Xc(Js(r),{retries:[]});return o===Xo?[r,t?.[r]??!0]:[r,o===Yc]}));return Cr.value=Object.fromEntries(n),Cr.at=Date.now(),Cr.value}async function qi(e,t=!0){let n=ph(e,{skipPermissions:t});if(await Xc(n)===Yo){Cr.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.":nf[r]?`Install it on this node: ${nf[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 vT(e,t=xo){return`${e.trim()}
|
|
204
204
|
|
|
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 ah(e){return e.role==="user"&&
|
|
206
|
-
`;return Qm=Qm.then(()=>K.mkdir(yn,{recursive:!0})).then(()=>K.appendFile(RB,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)||DB(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 DB(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 xT(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=>BB.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 vT(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 jB(e){let t=J.join(Ko(e),`${e.id}.ndjson`),n;try{n=await K.stat(t)}catch{return null}let r=dT.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 dT.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function LB(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([jB(e),LB(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:Fi.has(e.id)}}function ST(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-FB,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 TT(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=jy(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=ST(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 $y(f.box,f.sessionId),n=!0}}return n}async function ie(){let e=await X();await TT(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 re(e){let t=await X(),n=le(t,e);return n?(await TT(t)&&await Q(),Dt(n)):null}async function wh(e){let t=await F(e).catch(()=>[]),n;try{n=dr(t)}catch{return[]}return Vb(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(UB[e.agent]||[]).some(n=>n.test(t))}function ch(e){let t=(e.text||"").trim();return t?HB.some(n=>n.test(t)):!1}async function Su(e,t={}){let n=await re(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=[...Vv(n),...l],f={windows:[...wk(n),...gk(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 se=ri(_.agent).cliTools,ae=_.tools.filter(Te=>!se.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:jb(_)}: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=>!yk(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 se=g.get(_.box);se&&(_.text=`\u25A0 ${se}`)}}let x=await Promise.all(n.boxes.map(g=>Xk(g,t)));await bv(n,x.flat());let{agentTurnBoxes:S}=await gv(n,w);for(let g of n.boxes)S.has(g.name)&&g.cwd&&Fc(g.cwd);await G0(n),await eS(n,w).catch(()=>{});for(let g of n.boxes)g.agent==="codex"&&g.state==="live"&&await xv(n,g),g.agent==="cursor"&&g.state==="live"&&await Ev(n,g);let I=await Promise.all(n.boxes.map(g=>Yk(g,t)));await yv(n,I.flat().filter(g=>!th(g.box,g.ts,u)));let $=await F(n),D=c&&i.viewId?i.viewId:null,T=ox($),A=dr($).filter(g=>!(g.kind==="message"&&(ih(g)||ch(g)||ah(g)||Km(g)))&&!(g.kind==="view-card"&&D&&g.viewId===D)),R=sx(A,T),E=A.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&!R.has(g)&&th(g.box,g.ts,T))),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=Kb(Gb([...E,...L].filter(g=>g.ts!=null).sort((g,_)=>g.ts-_.ts))),oe=qB(n),G=oe.size?U.filter(g=>g.kind==="event"||!oe.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 _=p0(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&&V0(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)<WB;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>Kv&&!_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:L0(C?.agent)},pendingPrompt:B,working:_e,stopping:ET(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 qB(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 re(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 KB(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=Hb(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=l0(i,n||[]);await De(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=fb(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]:[]),S=[...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:S,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 KB(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=u0(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(()=>VB(e).catch(()=>{}),GB))}async function VB(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 AT(e,t,n,r){let o=uo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Ks(e,[n]),await Q()),$0(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 zB(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 AT(e,t,r,o);let a=Bi.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=kT(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=rT(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
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 ah(e){return e.role==="user"&&PB.test((e.text||"").trim())}async function X(){return Pt||(Zm||=(async()=>{try{Pt=JSON.parse(await K.readFile(kT,"utf8"))}catch{Pt={tracks:[]}}finally{Zm=null}return Pt})(),Zm)}async function Q(){await wt(kT,Pt)}function jB(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
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()}
|
|
207
207
|
|
|
208
|
-
${f}`:f}let d=ve(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||qs(e,null);try{if(await ur({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Js(n.agent),bailOnDialog:!0}),(Bi.get(e.id)||0)!==a){!o&&r.trim()&&(Ks(e,[r]),await Q());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}if(await
|
|
208
|
+
${f}`:f}let d=ve(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||qs(e,null);try{if(await ur({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Js(n.agent),bailOnDialog:!0}),(Bi.get(e.id)||0)!==a){!o&&r.trim()&&(Ks(e,[r]),await Q());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}if(await XB(e,t)){let f=Au(e.id),p={id:To(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};f.items.unshift(p),kh(e.id);let m=new Error("lead is blocked on a prompt \u2014 message queued");throw m.requeued={id:p.id,position:f.items.length},m}await Je(e.leadBox,i,{...sn(t?.agent,i),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),ve(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:d.at,text:r,...f?.interrupted?{reason:"interrupted"}:{},...f?.requeued?{reason:"requeued"}:{}}),f?.requeued)return{queued:!0,...f.requeued};throw f}await dj(e,t,i)||(await $T(e.leadBox,t?.agent,i.length+8,i).catch(()=>{}),ve(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:d.at,text:r,reason:"unsubmitted"}),o||Ks(e,[r])),c&&await ee({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function xh(e,{actions:t}={}){Au(e).items.push({id:To(),text:cx(t),ts:Date.now(),kind:"ship-notify"}),kh(e)}async function Bt(e,t,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(p=>p&&typeof p.rel=="string"):[];if((typeof t!="string"||!ms(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=le(await X(),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`);Hh(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=ms(t);await _T(s,a,i,n),Vi(e,"say");let c=await fe(s.leadBox);if(c&&await Ch(c)?await Ui(s,a,"say")?(await Q(),c=await fe(s.leadBox)):c=null:!c&&await Ji(s)&&(await Q(),c=await fe(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Au(e),u=await Oe(s.leadBox,{lines:ze}).catch(()=>""),d=l.items.length>0||ll(a?.agent,u);if(!d&&co(a?.agent,u)){let p=await $n(s.leadBox).catch(()=>u);d=!!br(a,p)}if(d){let p={id:To(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),kh(e),n||qs(s,null),await Q(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let f;try{f=await lh(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await Ui(s,a,"say-gate")||(await Q(),c=await fe(s.leadBox),!c))throw p;f=await lh(s,a,c,i,n,o)}return await Q(),f?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:f.position}:{ok:!0,leadBox:s.leadBox}}async function vh(e,{hint:t}={}){let n=le(await X(),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=Sm(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";zv(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${mT}
|
|
209
209
|
|
|
210
|
-
What to preview: ${String(t).trim()}`:
|
|
210
|
+
What to preview: ${String(t).trim()}`:mT;try{await Bt(e,a,{kind:"spin-up-view"})}catch(i){throw iu(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Eu(e){let t=le(await X(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);Bi.set(e,(Bi.get(e)||0)+1);let n=Gi(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Ws.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Ks(t,r),await Q()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=rj(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,So(ZB)]);let a=le(await X(),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 IT(e){let t=Ws.get(e);return!!t&&!t.canceled}function Vi(e,t){let n=Ws.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function Sh(e){Vi(e,"track-ended"),Bi.delete(e),Gi(e)}function rj(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}),Ws.set(e,r),(async()=>{let s=le(await X(),e),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await fe(s.leadBox):null,c=!1,l=!1;if(i){e:for(let y=0;y<QB&&!r.canceled;y++){await De(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let x of ej){await So(x);let T=await Oe(s.leadBox,{lines:ze}).catch(()=>"");if(!as(a?.agent,T)){c=!0;break e}}}if(!c){l=!0;for(let y=0;y<nj&&!r.canceled&&(await So(tj),!r.canceled);y++){let x=await Oe(s.leadBox,{lines:ze}).catch(()=>"");if(!as(a?.agent,x)){c=!0;break}await De(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=le(await X(),e)||s,d=u?.boxes.find(y=>y.name===u.leadBox)||a,f=null,p=null;u&&d&&i&&c&&!r.canceled&&(f=await ij(u,d).catch(()=>null),f||(p=await uj(u,d).catch(()=>null)));let m=u&&i&&!r.canceled?await sj(u).catch(()=>null):null,w=[f||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=Ks(u,w),await Q()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await oj(u||s,d,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await ee({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:d?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),Ws.get(e)===r&&Ws.delete(e),o()}),r}async function oj(e,t,n){await O(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function sj(e){let t=du(await F(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await O(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 ij(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 aj){await So(r);let o=await Oe(e.leadBox,{lines:ze}).catch(()=>""),s=za(t.agent,o);if(s&&ul(n.text,s))return await $T(e.leadBox,t.agent,n.text.length+8),ve(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function uj(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>cj||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Fe(r.text))return null;for(let s of lj){await So(s);let a;try{a=await lp(t,{})}catch{return null}if(Sb(a,r))return null;let i=await Oe(e.leadBox,{lines:ze}).catch(()=>"");if(za(t.agent,i))return null}return ve(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function dj(e,t,n){return Ya({sessionName:e.leadBox,agent:t?.agent,agentCmd:Js(t?.agent),text:n,trackId:e.id,flow:"say"})}async function $T(e,t,n,r=null){for(let o=0;o<3;o++){await Qo(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await So(200);let s=await Oe(e,{lines:ze}).catch(()=>""),a=za(t,s);if(!a||r&&!ul(r,a)&&!(t==="codex"&&Rf(r,a)))return!0;n=a.length+8}return!1}function qs(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 Ks(e,t){let n=[],r=o=>n.some(s=>Fe(s).includes(Fe(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?qs(e,n.join(`
|
|
211
211
|
|
|
212
|
-
`),{source:"restore"})?.text??null:null}function
|
|
212
|
+
`),{source:"restore"})?.text??null:null}function pj(e,t){let n=Fe(t);if(!n)return!1;if(vo(e.id).some(o=>Fe(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>fj)break;if(!(s.type!=="said"||!s.text||Fe(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Th(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=le(await X(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=ms(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&pj(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;Fe(o).includes(Fe(i))?qs(r,o):qs(r,`${i}
|
|
213
213
|
|
|
214
|
-
${o}`,{source:"restore"})}else qs(r,o);return await Q(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Ah(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=le(await X(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await fe(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=
|
|
215
|
-
`);return`${
|
|
214
|
+
${o}`,{source:"restore"})}else qs(r,o);return await Q(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Ah(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=le(await X(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await fe(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=H0(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await La(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await ur({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Js(o.agent)});let u=await W0(a,r.leadBox,i);return ve(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await Q(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function RT(e){for(let t of e){if(!t||!await hs(t))continue;let n=await b0(t).catch(()=>null);if(!n)continue;if((await K.stat(J.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Et(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:J.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function Ms(e){let t=[];for(let n of Et(e?.workspace)){if(!n?.dir)continue;let r=await oi(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await si(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function OT(e){let t=e;typeof t=="string"&&!J.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Ho(t)??await Cc(t));let n=await RT([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 mj(e,t,n,r){let o=`track/${Gh(t)}-${e}`,s=[];try{await K.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await OT(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await xe(c).catch(()=>null),u=J.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${J.basename(c)}-${p}`;a.add(u);let d=J.join(n,u),f=await ys(c);await gs(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await zi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await vt(i.base,i.dir).catch(()=>{});throw await K.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Eh(e,t){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 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 OT(typeof t=="string"?t.trim():t);if((o.repos??[]).some(f=>f?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await xe(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(f=>f?.name).filter(Boolean)]),c=J.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let f=2;i.has(c);f++)c=`${J.basename(s)}-${f}`;let l=J.join(o.dir,c),u=await ys(s);await k0(s,o.branch)?await El(s,l,o.branch):await gs(s,l,o.branch,u);let d={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),d],await zi(o.dir,o.repos,o.branch),ve(r,"retagged",{action:"add",repo:a??s,name:c,workspace:CT(o)}),await Q(),d}async function _h(e,t){let n=await X(),r=le(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 At.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 oi(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await si(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 vt(s.base,s.dir).catch(()=>{}),await K.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await zi(o.dir,o.repos,o.branch),ve(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:CT(o)}),await Q(),{removed:s.name}}async function hj(e,t,{repo:n,repos:r,cwd:o}={}){await K.mkdir(Ni,{recursive:!0});let s=J.join(Ni,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return mj(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!J.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Ho(n)??await Cc(n));let i=await RT([n,o,NB]);if(i){let c=`track/${Gh(t)}-${e}`;try{let l=await ys(i);await gs(i,s,c,l),await Li(s);let u=await xe(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await K.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await K.mkdir(s,{recursive:!0}),await Sp(s),await Li(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Li(e){try{await K.writeFile(J.join(e,"AGENTS.md"),Mi,{flag:"wx"})}catch{return}await bj(e,["/AGENTS.md"])}function yj(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
215
|
+
`);return`${gj}
|
|
216
216
|
|
|
217
217
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
218
218
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -224,20 +224,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
224
224
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
225
225
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
226
226
|
or preview an app, run it from that app's own repo directory.
|
|
227
|
-
`}async function zi(e,t,n){let r=
|
|
227
|
+
`}async function zi(e,t,n){let r=yj(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await K.writeFile(J.join(e,o),r).catch(()=>{})}async function NT(e){let{stdout:t}=await bT("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return J.resolve(e,t.trim())}async function bj(e,t){try{let n=await NT(e);await K.mkdir(J.dirname(n),{recursive:!0});let r=await K.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
228
228
|
`).includes(s));o.length&&await K.appendFile(n,(r.endsWith(`
|
|
229
229
|
`)||!r?"":`
|
|
230
230
|
`)+o.join(`
|
|
231
231
|
`)+`
|
|
232
|
-
`)}catch{}}async function
|
|
232
|
+
`)}catch{}}async function kj(e,t){try{let n=await NT(e),r=await K.readFile(n,"utf8"),o=r.split(`
|
|
233
233
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
234
234
|
`).length&&await K.writeFile(n,o.join(`
|
|
235
|
-
`))}catch{}}async function Ih(){let e={refreshed:0,removed:0,codexGlobal:null};try{await K.mkdir(yn,{recursive:!0}),await K.writeFile(J.join(yn,"CLAUDE.md"),
|
|
235
|
+
`))}catch{}}async function Ih(){let e={refreshed:0,removed:0,codexGlobal:null};try{await K.mkdir(yn,{recursive:!0}),await K.writeFile(J.join(yn,"CLAUDE.md"),wj).catch(()=>{}),await K.writeFile(J.join(yn,"AGENTS.md"),Mi).catch(()=>{});let t=process.env.CODEX_HOME||J.join(process.env.HOME||"/tmp",".codex");if((await K.stat(t).catch(()=>null))?.isDirectory()){let r=J.join(t,"AGENTS.md"),o=await K.readFile(r,"utf8").catch(()=>null);o===null?(await K.writeFile(r,rh,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):nh(o)&&o!==rh&&(await K.writeFile(r,rh).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Ni,zs]){let o=await K.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=J.join(r,s.name),i=J.join(a,"AGENTS.md"),c=await K.readFile(i,"utf8").catch(()=>null);c!==null&&nh(c)&&c!==Mi&&(await K.writeFile(i,Mi).catch(()=>{}),e.refreshed++);let l=J.join(a,"CLAUDE.md"),u=await K.readFile(l,"utf8").catch(()=>null);u!==null&&nh(u)&&(await K.rm(l,{force:!0}).catch(()=>{}),await kj(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function PT(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Et(t))n.base&&n.dir&&await vt(n.base,n.dir).catch(()=>{});await K.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await vt(t.base,t.dir):await K.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function _u({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 qi(o,s),i=await X(),c=To().slice(0,8),l=await hj(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),d=`${e.trim()} lead`;u.has(Zc(d))&&(d=`${e.trim().slice(0,50).trim()} ${c} lead`);let f=await cr(d,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0}),p={id:c,name:e.trim(),cwd:f.cwd,workspace:l,leadBox:f.name,boxes:[jh(f,"lead","live")],events:[],createdAt:Date.now()};return ve(p,"opened",{agent:o,box:f.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:m,base:w,baseRef:y,repo:x})=>({name:m,base:w,baseRef:y,repo:x}))}:{}}}),i.tracks.push(p),await Q(),Ka({cwd:f.cwd,sessionName:f.name,agent:o,agentCmd:a,onReady:async()=>{let m=p.boxes.find(w=>w.name===p.leadBox);!m||m.readyAt||(m.readyAt=Date.now(),await Q())}}),Dt(p)}async function $h({provider:e="claude",fresh:t=!1}={}){if(!xj.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await X()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Dt(r);if(u.boxes.find(f=>f.name===u.leadBox)?.state==="live")return u}await Ao(r.id).catch(()=>{})}let o=(await qi(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=To().slice(0,8),a=J.join(yn,"setup",s);await K.mkdir(a,{recursive:!0});let i=await cr(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&Ka({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:[jh(i,"lead","live")],events:[],createdAt:Date.now()};return ve(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 X()).tracks.push(c),await Q(),Dt(c)}async function Rh(e,t,n){await Hd(t.cwd).catch(()=>{}),await tS(e).catch(()=>{});let r=Vs.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Vs.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of Eo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Jh(a).catch(()=>{}),At.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 X(),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 Ht(a.name).catch(()=>{})}async function Oh(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await X(),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 SS(s),Sh(e),await Rh(e,s,"track deleted"),n?await eT(e).catch(()=>{}):await QS(e).catch(()=>{}),await G0(),await PT(s),t&&await TS(e).catch(()=>{}),t?await US(e).catch(()=>{}):await FS(s).catch(()=>{}),ve(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await Q(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Ao(e){let t=await X(),n=le(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(),Sh(e),ve(n,"archived",{boxes:n.boxes.length}),await Q(),n.kind==="setup"?(await DT(e),Dt(n)):(Fi.add(e),Tj(e).catch(()=>{}).finally(()=>Fi.delete(e)),{...await Dt(n),archiving:!0})}async function Tj(e){try{await Aj(e)}catch{}await DT(e).catch(()=>{})}async function DT(e){let t=await X(),n=le(t,e);if(!(!n||!n.archivedAt)){await Rh(e,n,"track archived");for(let r of n.boxes)Mr.has(r.state)||(r.state="stopped");await Q()}}async function Aj(e){let t=await X(),n=le(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 fe(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await wk(n.leadBox,i.cwd,{timeoutMs:Sj})),s="lead-cli"}catch{}if(r||(r=Ok(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 O(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 Q()}async function Ln(e,t){let n=await Qe(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await BT(n);if(r.length){for(let o of r)await fo(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Ao(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function BT(e){return(await po().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 Fn(e,t){return Iu(e,t,"merged")}async function Iu(e,t,n="merged"){let r=await Qe(e,t).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await BT(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await yx(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await Bt(r.trackId,jT(a),{kind:"merge-notify"}),!0}catch{return!1}}function jT(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 ${hT[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 ${hT[i.outcome]??i.outcome}`).join(`
|
|
236
236
|
`),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
237
|
${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=Sj(e,r,{freshSession:t,reason:n}).finally(()=>oh.delete(r.name));return oh.set(r.name,s),s}async function Sj(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 re(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 mT(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 BT(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=BT(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:xT(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}),Tj(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 Tj(e,t,n,r,o){let s=async(D,T={})=>{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:T.sessionName,...T})};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:bT})}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=yb(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 T=await X(),A=le(T,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 S=Date.now(),I=Gs(x),$=I[I.length-1];$.endedAt=S,e.reason&&($.endReason=e.reason),I.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:S,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:S},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),Aj(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 Aj(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:bT})}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(d)}e.step="wip";let a=await zm({id:t.id,cwd:t.cwd},{force:!0}),i=await X(),c=le(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Gs(l),await Q(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:d,baseSha:f})=>({name:u,repo:d,baseSha:f})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:f})=>({name:u,repo:d,error:f})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Hh(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 Ou(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await X(),o=le(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Sh(e),await Rh(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Il(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Jm({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,ve(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await Q(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function Wh(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Ou(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
|
|
240
|
-
`)[0]}else l?.sha&&(E="wip snapshots cover single-repo worktrees only");await lT({id:e,cwd:I}).catch(()=>({restored:!1}));let U=J.join(I,xo),oe=null;(await qc(I)).restored?oe="checkpoint":typeof u=="string"&&u.trim()&&(await K.mkdir(J.dirname(U),{recursive:!0}),await K.writeFile(U,u),oe="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,...oe?{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:oe}}}catch(A){for(let R of T)await vt(R.base,R.dir).catch(()=>{});throw await K.rm(I,{recursive:!0,force:!0}).catch(()=>{}),A}}async function jT(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 LT(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 FT(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function Rj(e,t,n){if(!FT(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),HT(s,o).catch(a=>UT(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 Rj(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=LT(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 wT(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 wT(n,r.name,o)}}return t=await Oj(e)||t,t&&await Q(),t}async function wT(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 Oj(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:xT(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),Cj(f,c,s).catch(p=>UT(f,p)),f}async function UT(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 Dj(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 Cj(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 Mj(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 Nj(e,o),await vT(e.trackId,o.name,MB(e.task,!!e.repos),{kind:"subtask"}),await HT(e,t)}async function HT(e,t){await Hs(e,"working");let n=J.join(e.dir,bu);if(await jj(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:mT({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:mT({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 Bj(e,t,n);await Hs(e,"resume"),await vT(e.trackId,e.leadBox,NB(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 jT(e.trackId,e.id,{...Cu(e),finishedAt:o}),await Pj(e,o),e.finishedAt=o}async function Mj(e,t){let n=J.join(e.dir,J.dirname(Pi));await K.mkdir(n,{recursive:!0});let r="";try{r=await tb(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}
|
|
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(d)}e.step="wip";let a=await zm({id:t.id,cwd:t.cwd},{force:!0}),i=await X(),c=le(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Gs(l),await Q(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:d,baseSha:f})=>({name:u,repo:d,baseSha:f})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:f})=>({name:u,repo:d,error:f})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Hh(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 Ou(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await X(),o=le(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Sh(e),await Rh(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Il(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Jm({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,ve(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await Q(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function Wh(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Ou(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 gT(e,t,n){try{await El(e,t,n)}catch{await Jt(e,["worktree","prune"]).catch(()=>{}),await El(e,t,n)}return t}async function qh(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(sh.has(t))throw new Error("an adopt is already running for this Track");sh.set(t,{startedAt:Date.now()});try{return await $j({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{sh.delete(t)}}async function $j({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:d=[],createdAt:f=null,leadBox:p=null,reason:m=null,downSince:w=null}){let y=await qi(n,r),x=await X(),T=le(x,e);if(T&&!T.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);T&&(await Jm({id:T.id,cwd:T.cwd}).catch(()=>{}),await PT(T),x.tracks.splice(x.tracks.indexOf(T),1),await Q()),await K.mkdir(Ni,{recursive:!0});let I=J.join(Ni,e);await K.rm(I,{recursive:!0,force:!0}).catch(()=>{});let $=null,D=!1,S=[];try{let A=[];if(i)for(let B of Array.isArray(c)?c:[]){let ye=typeof B=="string"?B:B?.repo;if(!ye||!/^[^/\s]+\/[^/\s]+$/.test(ye))continue;let be=await Ho(ye)??await Cc(ye);await Jt(be,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],dh),A.push({slug:ye,base:be,name:typeof B=="object"?B?.name??null:null})}if(i&&A.length===1){let{base:B,slug:ye}=A[0];await gT(B,I,i),S.push({base:B,dir:I}),await Li(I);let be=await ys(B);$={dir:I,base:B,branch:i,kind:"worktree",baseRef:be,repo:ye}}else if(i&&A.length>=2){await K.mkdir(I,{recursive:!0});let B=new Set([".bostrat"]),ye=[];for(let be of A){let _e=be.name||J.basename(be.base);B.has(_e)&&(_e=be.slug.replace("/","-"));for(let h=2;B.has(_e);h++)_e=`${J.basename(be.base)}-${h}`;B.add(_e);let Ie=J.join(I,_e);await gT(be.base,Ie,i),S.push({base:be.base,dir:Ie});let b=await ys(be.base);ye.push({name:_e,dir:Ie,base:be.base,baseRef:b,repo:be.slug})}await zi(I,ye,i),$={dir:I,kind:"multi",branch:i,repos:ye}}else D=!0,await K.mkdir(I,{recursive:!0}),await Sp(I),await Li(I),$={dir:I,base:null,branch:null,kind:"repo"};let R=!1,E=null,L=null;if(l?.sha&&$.kind==="worktree")try{await Jt(I,["fetch","origin",l.ref||Vm(e)],dh);let{stdout:B}=await Jt(I,["rev-parse","FETCH_HEAD^"]),{stdout:ye}=await Jt(I,["rev-parse","HEAD"]),be=B.trim()===ye.trim();be||await Jt(I,["merge-base","--is-ancestor",ye.trim(),B.trim()]).then(()=>!0,()=>!1)&&(await Jt(I,["reset","--hard",B.trim()]),L={from:ye.trim(),to:B.trim()},be=!0),be?(await Jt(I,["read-tree","--reset","-u","FETCH_HEAD"]),await Jt(I,["reset","-q"]),R=!0):E="snapshot does not parent the fetched branch head"}catch(B){E=String(B?.message||B).split(`
|
|
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}
|
|
241
241
|
|
|
242
242
|
**Delegated task:** ${e.task}
|
|
243
243
|
|
|
@@ -248,7 +248,7 @@ ${a}`}async function Ch(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
248
248
|
## Conversation so far (lead \u2192 you)
|
|
249
249
|
|
|
250
250
|
${r}
|
|
251
|
-
`;await K.writeFile(J.join(e.dir,Pi),o)}async function
|
|
251
|
+
`;await K.writeFile(J.join(e.dir,Pi),o)}async function Bj(e,t){let n=await X(),r=le(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:wa(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),ve(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]||{},...Cu(e)},await Q())}async function jj(e,t=e.finishedAt){let n=await X(),r=le(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),ve(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 Q()}async function Lj(e){let t=await X(),n=le(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"),ve(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]||{},...Cu(e)},await Q()}async function Fj(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=J.join(t.cwd,r);await K.mkdir(J.dirname(o),{recursive:!0}),await K.copyFile(n,o)}catch{}return r}async function Jh(e){if(e.dir&&await Hd(e.dir).catch(()=>{}),e.box&&await Ht(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await vt(t.leadDir,J.join(e.dir,t.name)).catch(()=>{});await K.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await fe(e.leadBox).catch(()=>null))?.cwd;n&&await vt(n,e.dir).catch(()=>{})}}async function Uj(e,t){let n=(t.startedAt||Date.now())+Rj;for(;;){if(t.canceled||t.forceMerge)return;try{await K.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await So(Oj)}}function So(e){return new Promise(t=>setTimeout(t,e))}var bT,yn,kT,MB,Ni,NB,xu,Wi,Cr,xo,xT,PB,bu,Pi,DB,BB,Pt,Zm,le,Qm,ve,eh,th,FB,Mr,pT,WB,qB,KB,GB,JB,Di,mT,ZB,QB,ej,tj,nj,Ws,Bi,aj,cj,lj,fj,CT,ji,MT,wj,Mi,nh,gj,rh,xj,Fi,vj,Sj,hT,oh,uh,jh,Vs,Ys,dh,Jt,fh,Fh,sh,Rj,Oj,zs,Us,At,Cj,Eo,Cu,Yi,Gh,Hi,_r=j(()=>{Aa();nt();Jf();Ck();Ga();rr();sl();Bk();it();rn();Lo();ol();e0();eo();gp();bp();g0();dl();Of();al();Hf();qt();_l();tr();Cn();Va();Cl();q0();Np();vv();qf();Bf();Tv();um();ue();su();uu();gi();Ss();$m();js();Lm();gu();Gm();Pn();aT();Ym();fT();Kc();bT=CB(OB),yn=process.env.BOSTRAT_STATE_DIR||J.join(process.env.HOME||"/tmp",".bostrat"),kT=J.join(yn,"tracks.json"),MB=J.join(yn,"events.jsonl"),Ni=J.join(yn,"workspaces"),NB=process.env.BOSTRAT_DEFAULT_REPO||null,xu={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"}},Wi=Object.keys(xu);Cr={at:0,value:null};xo=".bostrat/handoff/latest.md",xT=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;PB=/\(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\.\)$/;bu=".bostrat/handoff/subtask-result.md",Pi=".bostrat/handoff/lead-context.md",DB=(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 ${Pi} \u2014 read that first for context. Your scoped task:
|
|
252
252
|
|
|
253
253
|
${e}
|
|
254
254
|
|
|
@@ -256,7 +256,7 @@ Work ONLY inside this directory. cd into the repo a change belongs to before run
|
|
|
256
256
|
|
|
257
257
|
${e}
|
|
258
258
|
|
|
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 ${bu} 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.`,
|
|
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 ${bu} 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.`,BB=(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."),Pt=null,Zm=null;le=(e,t)=>e.tracks.find(n=>n.id===t)||null,Qm=Promise.resolve();ve=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),jB(e.id,r),V0(e,r),r};ib(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await X()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(ve(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await Q().catch(()=>{}))});eh=J.join(yn,"plane-link","update-notice.json");th=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),FB=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Mr=new Set(["merged","conflict","canceled","failed","handed-off"]),pT=new Map;WB=5e3;qB={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};KB=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];GB=10*6e4;JB=1500,Di=new Map;mT="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.";ZB=4e3,QB=3,ej=[300,350,400],tj=1e3,nj=12,Ws=new Map,Bi=new Map;aj=[250,400,500,600];cj=3e4,lj=[350,650,900];fj=12e4;CT=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}))});ji="# bostrat workspace",MT=`${ji}
|
|
260
260
|
|
|
261
261
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
262
262
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -542,16 +542,16 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
542
542
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
543
543
|
implementation turn this way, every time. When the operator has already asked you
|
|
544
544
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
545
|
-
`,
|
|
546
|
-
`)),
|
|
545
|
+
`,wj=MT,Mi=MT+Kb;nh=e=>typeof e=="string"&&(e===ji||e.startsWith(`${ji}
|
|
546
|
+
`)),gj="# bostrat multi-repo workspace";rh=Mi.replace(`${ji}
|
|
547
547
|
`,`${ji}
|
|
548
548
|
|
|
549
549
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
550
550
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
551
551
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
552
552
|
When working anywhere else, ignore everything below.
|
|
553
|
-
`);yj=new Set(["claude","codex","cursor"]);Fi=new Set,bj=e=>Fi.has(e);kj=9e4;pT={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)=>gT("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),fh=new Map,Fh=e=>fh.get(e)||null;sh=new Map;_j=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,Ij=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,zs=J.join(yn,"worktrees"),Us=[Pi,bu],At=new Map,$j=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=>jT(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 rH from"node:readline";import{fileURLToPath as oH}from"node:url";import Lt from"express";import sH from"node:http";import aH from"node:https";import hd from"node:os";import{WebSocketServer as iH}from"ws";nt();import DR from"node-pty";async function My(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=Cy(n.cols,120,Ny),o=Cy(n.rows,32,Py),s;try{s=DR.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"&&FR(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{}LR(t)})}var BR=6,jR=250;async function LR(e,t=BR,n=jR){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 Ny=20,Py=6;function FR(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Ny&&Number(t)>=Py}function Cy(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 Dy(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 cH}from"node:module";var Lj=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(Lj)&&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 tA from"node:fs";import Pr from"node:fs/promises";import nL from"node:os";import tt from"node:path";import{spawn as rL}from"node:child_process";import{createRequire as oL}from"node:module";ue();Cl();import ZT from"node:fs";import Zi from"node:fs/promises";import Hj from"node:http";import Xt from"node:path";import Pu from"node:os";import{createRequire as Wj}from"node:module";import Zh from"ws";import Fj from"node:path";import Uj from"node:os";var Xi=process.env.BOSTRAT_STATE_DIR||Fj.join(process.env.HOME||Uj.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 qT(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 KT(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 Wj(import.meta.url)("../package.json").version||null}catch{return null}})(),GT=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=ZT.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 qj({home:e=process.env.HOME||Pu.homedir(),env:t=process.env}={}){let n=r=>{try{return ZT.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,VT=1e3,zT=3e4,Kj=6e4,Gj=1500,Vj=500,Nr=100,zj=2e3,Jj=25e3,Yj=6e4,QT=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},Xj=QT(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),Zj=3e4,JT=10*6e4,Qj=400,YT=250,eL=QT(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Qh=4,tL=6e4,XT=6e4;function eA({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=tL,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:S=Xj,unackedRetryMs:I=Yj,pingMs:$=Jj,maxInflightEvents:D=Qj,httpTimeoutMs:T=eL,stallBackoffMinMs:A=Zj,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=KT(e);let L=m.replace(/^http/,"ws"),U=new Set,oe=new Map,G=new Map,C=Xt.join(GT,"plane-link","cursor.json"),Z=Xt.join(GT,"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,se=Nr,ae=new Set,Te=[],Ee=null,Le=null,bt=!1,bd=!1,da=VT,fa=null,Do=null,Bo=null,vn=null,Sn=null,kd=!1,Ac=0;async function mI(){try{te=JSON.parse(await Zi.readFile(C,"utf8"))}catch{te={}}}function hI(){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)}}},zj),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"?(bI(N),!0):Ze({v:et,ch:"events",type:"append",events:N})}function wI(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=wI(N),M=k==null?P:Math.min(P,k-1);M>(te[N]??0)&&(te[N]=M,hI())}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<S||V!=null&&N-V<S||Ie==="http"&&h||ye>N)return;B++;let k=Math.min(A*2**(B-1),JT);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"?se: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 gI(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+=se)pa(M.slice(H,H+se))}Te.length&&!Ee&&(Ee=setTimeout(Td,YT),Ee.unref?.())}let yI=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 bI(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:yI(N)}),signal:AbortSignal.timeout(T)}),M=(Number(k.headers.get("retry-after"))||0)*1e3;if(k.status===200){let H=await k.json();se=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){se=Math.max(1,Math.floor(N.length/2)),E.error(`[plane-link] ingest request too large (${N.length} events) \u2014 halving batches to ${se}`),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>=XT){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){jo(N,k?.name==="TimeoutError"||k?.name==="AbortError"?"timeout":"network",5e3,{progress:!1})}finally{b--;for(let k of P)ae.delete(k)}}async function kI(){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(T)});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()+T+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),JT);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:se}: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 xI(){i&&(Sn&&clearInterval(Sn),mg(),Sn=setInterval(mg,c),Sn.unref?.())}async function vI(){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"?se:Nr;for(let Se=0;Se<H.length;Se+=pe){if(!await gI(P.id))return;let Pe=H.slice(Se,Se+Math.min(pe,Ie==="http"?se: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,YT),Ee.unref?.()))}let ha=new Map;async function SI(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 TI(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 TI(N,P,k){let{reqId:M,method:H,path:pe,body:Se}=N,Pe=new URL(m);return new Promise(pt=>{let $t=Hj.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":EH,"transfer-encoding":_H,upgrade:IH,...AI}=Ge.headers;Ze({v:et,ch:"control",type:"http_head",reqId:M,status:Ge.statusCode||502,headers:AI}),Ge.on("data",EI=>{Ze({v:et,ch:"control",type:"http_data",reqId:M,data:Buffer.from(EI).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:qj(),...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=VT,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,se=Nr,vn&&clearInterval(vn),vn=setInterval(()=>{Ze({v:et,ch:"ctl",type:"ping"}),fg(),kI().catch(()=>{}),Sd(),Te.length&&!Ee&&Td()},$),vn.unref?.(),xI(),vI().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=zT,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"){SI(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);oe.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),oe.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=oe.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=oe.get(k.streamId);oe.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 oe.values())M.close();oe.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<Kj,k=Math.max(P?Gj+Math.random()*Vj:da,ye-Date.now());Do=setTimeout(()=>{Do=null,wg()},k),Do.unref?.(),P||(da=Math.min(da*2,zT))};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>=XT&&(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),mI().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 oL(import.meta.url)("../package.json").version||null}catch{return null}})(),sL=process.env.BOSTRAT_STATE_DIR||tt.join(process.env.HOME||nL.tmpdir(),".bostrat"),nA=tt.join(sL,"self-update"),Bu=tt.join(nA,"status.json"),aL=1440*6e4,iL=6e4,ec=new Set(["npm-global","git-checkout"]);function rA({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"||cL(t)?"auto":"prompt":"off"}function cL(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var _t=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function lL({kind:e,to:t,from:n=Du,statusFile:r=Bu,logFile:o=tt.join(nA,"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=`
|
|
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=`
|
|
555
555
|
health() {
|
|
556
556
|
for i in $(seq 1 45); do
|
|
557
557
|
sleep 1
|
|
@@ -575,7 +575,7 @@ write_status() { # <status> [error]
|
|
|
575
575
|
}
|
|
576
576
|
${p}
|
|
577
577
|
`;if(e==="npm-global"){let y=a?tt.join(a,"bin","npm"):"npm",x=a?`export PATH=${_t(tt.join(a,"bin"))}:"$PATH"
|
|
578
|
-
`:"",
|
|
578
|
+
`:"",T=a?` --prefix ${_t(a)}`:"",I=$=>`${_t(y)} install -g "bostrat-node@${$}" --no-audit --no-fund${T}`;return m+x+`
|
|
579
579
|
${d("updating",'"" ')}
|
|
580
580
|
if ! ${I("$TO")}; then
|
|
581
581
|
${d("failed",'"npm install failed"')}
|
|
@@ -616,64 +616,64 @@ if [ -n "$FROM" ] && ${w("$FROM")}; then
|
|
|
616
616
|
fi
|
|
617
617
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
618
618
|
exit 1
|
|
619
|
-
`}async function tc(e=Bu){try{return JSON.parse(await Pr.readFile(e,"utf8"))}catch{return null}}var uL=new Set(["updating","restarting","rolling-back"]),ju=e=>!!(e&&uL.has(e.status));async function Lu({to:e,from:t=Du,kind:n=_o(),reason:r="manual",statusFile:o=Bu,spawnImpl:s=rL,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 dL())||tt.resolve(tt.dirname(process.execPath),".."):null,d=lL({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=tA.existsSync("/run/systemd/system")&&await fL("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 dL(){try{return await Pr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function fL(e){for(let t of String(process.env.PATH||"").split(tt.delimiter))try{return await Pr.access(tt.join(t,e),tA.constants.X_OK),!0}catch{}return!1}var pL=45*6e4;async function oA({statusFile:e=Bu,current:t=Du,now:n=Date.now,staleMs:r=pL}={}){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 sA({isBusy:e,start:t=Lu,onStuck:n=null,onStarted:r=null,tickMs:o=iL,stuckMs:s=aL,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 mL=(...e)=>fetch(...e);function hL(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ew(e=process.env){if(!hL(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 mL(`${$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 wL=(...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 wL(`${$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 gL(e,t=process.env){return Uu("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function aA({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await gL(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 iA(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 yL=(...e)=>fetch(...e);async function bL(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 yL(`${$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 cA=72,rw=160;async function lA(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>cA)throw new Error(`the title is the choice as a LABEL \u2014 ${cA} 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 uA(e,t=process.env){return bL("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Gm();ue();import ic from"node:fs/promises";import Yu from"node:path";import lF from"node:os";rr();import kL from"node:fs/promises";import dA from"node:path";import xL from"node:os";var fA=()=>dA.join(process.env.BOSTRAT_STATE_DIR||dA.join(process.env.HOME||xL.tmpdir(),".bostrat"),"integrations.json");async function Xn(){try{return JSON.parse(await kL.readFile(fA(),"utf8"))||{}}catch{return{}}}var Hu=["linked","prOpened","prMerged","create"],ow=["prompt","auto","off"],vL={create:["prompt","off"]},pA=e=>vL[e]??ow;function Wu(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Hu){let s=pA(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 mA(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=pA(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(fA(),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 SL(){let e=(await Xn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var TL={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Ku(){let t=(await Xn()).voiceChat||{},n=t.provider||"openai",r=TL[n];if(!r)return null;let o=n==="openai"?await SL():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function hA(){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 TA from"node:path";import JL from"node:os";import{randomUUID as AA}from"node:crypto";var iw={};Ad(iw,{createComment:()=>ML,findIssueUrl:()=>IL,issueStates:()=>DL,mdToAdf:()=>yA,moveIssue:()=>BL,parseIssueRef:()=>_L,resolveIssue:()=>$L,uploadImage:()=>NL,upsertCard:()=>RL,whoami:()=>aw});var Zs=e=>String(e.baseUrl||"").replace(/\/$/,""),gA=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:gA(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 AL=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,EL=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function _L(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(AL);return r?r[1].toUpperCase():null}let n=t.match(EL);return n?n[1]:null}function IL(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 $L(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 RL(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 OL=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function CL(e){let t=[],n=0;for(let r of e.matchAll(OL)){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 yA(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(...
|
|
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(...PL(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 DL(e,t,n){let r=await Ro(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:kA(n)});return r?.id?`${Zs(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function aw(e){let t=await Ro(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Zs(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function BL(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(`${Zs(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:bA(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??`${Zs(e)}/rest/api/3/attachment/content/${c.id}`}var jL={new:"unstarted",indeterminate:"started",done:"completed"},yA=e=>jL[e?.key]??"started";async function LL(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Ro(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Ro(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:yA(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:yA(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function FL(e,t,n){await Ro(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var dw={};Ad(dw,{createComment:()=>zL,createIssue:()=>uw,createProject:()=>lw,findIssueUrl:()=>KL,issueStates:()=>YL,moveIssue:()=>XL,parseIssueRef:()=>qL,resolveIssue:()=>GL,teams:()=>Qs,uploadImage:()=>JL,upsertCard:()=>VL,whoami:()=>cw});var UL=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function kn(e,t,n={}){let r=await fetch(UL(),{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 HL=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,WL=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function qL(e){let t=String(e||"").trim(),n=t.match(HL);if(n)return n[1].toUpperCase();let r=t.match(WL);return r?r[1]:null}function KL(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 GL(e,t){let r=(await kn(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 VL(e,t,{url:n,title:r,subtitle:o}){let s=await kn(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 zL(e,t,n){let r=await kn(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 cw(e){let t=await kn(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 JL(e,{filename:t,contentType:n,buffer:r}){let o=await kn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
621
621
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
622
622
|
success
|
|
623
623
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
624
624
|
}
|
|
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 VL(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 zL(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 bA=255;async function lw(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>bA,a=s?`${o.slice(0,bA-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 EA={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(EA)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Oo(e){let t=EA[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||TA.join(process.env.HOME||JL.tmpdir(),".bostrat"),hw=()=>TA.join(_A(),"tickets.json"),IA=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Gu=e=>`${IA()}/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 $A(){return{...await ac()}}async function RA(){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 MA(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 OA(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 YL(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 XL(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 OA(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Gu(e),title:`Bostrat Track: ${t.track.name}`,subtitle:XL(t,r)})}function Vu(e){let t=new URL(IA());return`${t.protocol}//${e}.${t.host}/`}var ZL={linked:"linked",opened:"prOpened",merged:"prMerged"};function QL(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 CA(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 MA(e,t,n){let r=await Oo(t);if(!r)return;let o=r.cfg.ticketFlow?.[ZL[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=bw(s,a),c=QL(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 CA(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:AA(),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 NA(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 CA(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function PA(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 DA(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 kA=50;async function BA(){let e=await Io();if(!e)throw new Error("no ticketing integration configured");return Qs(e)}function eF(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>kA)throw new Error(`too many issues (${n.length}); cap is ${kA} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function tF(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 jA(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=eF(t),o=await Qs(n),s=await tF(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=AA();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 LA(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 nF(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 xA=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),mw=new Map;function rF(e,t){if(xA()<=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(()=>{})},xA());n.unref?.(),mw.set(e,n)}var vA=new Map;async function SA(e,t,n){let r=vA.get(e)??0;Date.now()-r<600*1e3||(vA.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 oF(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 SA(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 OA(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?`
|
|
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?`
|
|
626
626
|
|
|
627
|
-
Agent cost for this Track: ${
|
|
627
|
+
Agent cost for this Track: ${QL(o)}.`:"")),await PA(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await sF(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await rc(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
628
628
|
|
|
629
629
|
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await rc(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 rc(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Vu(r.slug)}
|
|
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:`<${Gu(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Iw(e,t){await Co(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:
|
|
630
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await aF(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await AA(e.trackId,s?.cwd??null,o)}}function HA({resolveTrack:e}){ht.on("event",t=>{iF(t,{resolveTrack:e}).catch(()=>{})})}async function WA(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await ac(),r=0;for(let[o,s]of Object.entries(n))await ct("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}Ss();js();import cF from"ws";var qA=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function zu(e,t,n={}){let r=await fetch(`${qA()}/${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 vw=(e,t)=>zu("chat.postMessage",e,t),Co=(e,t)=>zu("chat.update",e,t);async function dt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function lF(e,t,n={}){let r=await fetch(`${qA()}/${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 KA(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await lF("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 zu("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var uF=2200;function GA({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 zu("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new cF(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let f=w=>{try{o.send(JSON.stringify(w?{envelope_id:d.envelope_id,payload:w}:{envelope_id:d.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,f()},uF);Promise.resolve().then(()=>t(d)).then(w=>{clearTimeout(m),!p&&(p=!0,f(d.accepts_response_payload?w:void 0))},w=>{clearTimeout(m),p||(p=!0,f()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var JA=()=>process.env.BOSTRAT_STATE_DIR||Yu.join(process.env.HOME||fF.tmpdir(),".bostrat"),_w=()=>Yu.join(JA(),"slack.json"),Ju=null,Sw=null,Tw=Promise.resolve();async function $w(){return Ju||(Sw??=ic.readFile(_w(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>Ju=e).finally(()=>Sw=null),Sw)}async function VA(){let e=JSON.stringify({threads:Ju},null,2),t=`${_w()}.${process.pid}.tmp`;await ic.mkdir(JA(),{recursive:!0}),await ic.writeFile(t,e),await ic.rename(t,_w())}async function Zt(){Tw=Tw.then(VA,VA),await Tw.catch(()=>{})}async function Rw(e){return(await $w())[e]??null}async function pF(e,t){let n=await $w();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var he=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function cc(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function mF(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return cc(n.replace(/\n+/g," "),t)}function YA(e){let t=`*${he(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${he(e.repoLabel)} \xB7 \`${he(e.branch||"?")}\` \xB7 lead *${he(e.lead)}*`,r=e.kickoff?`> ${he(cc(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
631
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Gu(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Iw(e,t){await Co(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:YA(t)})}async function Lr(e,t,n,r=void 0){return vw(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var zA=new Set;function jr(e){zA.add(e),e.catch(()=>{}).finally(()=>zA.delete(e))}var Ce=e=>({response_type:"ephemeral",text:e}),hF="*/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._",wF=["claude","cursor","codex"];function Aw(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 Ew=(e,t)=>Ce(`No live Track matches \u201C${he(t)}\u201D. Live: ${e.map(n=>`\`${he(n.name)}\``).join(" \xB7 ")||"none"}`);async function XA(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=t.maxConcurrent){await dt(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 dt(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?Yu.basename(c.base):"fresh workspace"),lead:"claude"},u=await vw(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:YA(l),unfurl_links:!1});l.ts=u.ts,(await $w())[i.id]=l,await Zt(),await dt(o,{...Ce(`\u2713 *${he(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await dt(o,{...Ce(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function gF(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Rw(n.id);s&&(s.archivedAt=Date.now(),await Zt(),await Iw(t,s).catch(()=>{})),await dt(r,{...Ce(`\u2713 *${he(n.name)}* archived.`),replace_original:!0})}catch(s){await dt(r,{...Ce(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function yF(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await dt(o,{...Ce(`\u2713 Handing *${he(n.name)}* to ${he(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await dt(o,{...Ce(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function bF(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 dt(r,{...Ce(`\u2713 Stopped *${he(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 dt(r,{...Ce(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function kF(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:cc(`${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 *${he(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function xF(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?Yu.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 *${he(s.name)}* \u2014 ${he(i)}${a.branch?` \`${he(a.branch)}\``:""} \xB7 lead ${he(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
632
632
|
`)+(o?`
|
|
633
|
-
_${o} archived_`:"")}async function
|
|
634
|
-
\u2713 *${he(a)}*`}}]}).catch(()=>{})}}catch(c){await dt(n.response_url,Ce(`Couldn't answer: ${c?.message||c}`))}}async function
|
|
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 LF(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 FF=new Set(["Write","Edit","NotebookEdit"]),UF=220,HF=160;function WF(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(FF.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,UF)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Jr(o.command.trim().replace(/\s+/g," ")).slice(0,HF));return r}function qF(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 XA(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:WF(e,o)});break;case"Stop":await KF(r,o,e,i);break;default:break}return a}async function KF(e,t,n,r){let o=[],s=qF(n,t);if(s)try{o=jF(await BF.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=LF(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 GF}from"node:child_process";import{promisify as VF}from"node:util";var zF=VF(GF),Ow=(e,t)=>zF("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4});function ZA(e,t=Date.now()){return Bd(e,t)??0}function JF(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 QA(e,t,{now:n=Date.now()}={}){let r=ZA(t,n),o=await F(e);return ou({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:JF(o,t.name,r)})}function YF(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 XF(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 ZF(e){try{let{stdout:t}=await Ow(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function eE(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=ZA(t,i),l=await XF(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 ZF(r),...l,ts:i}}),d=null,f=!1;try{let p=await Nv(r,n,{fixKey:Ns({trackId:e.id,boxId:t.name,epoch:c}),window:$r,context:{...YF(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=Pv(p);if(m>=Mv){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 tE(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 nE=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),rE=(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=nE(),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);rE(u,w.totalTokens);for(let[y,x]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:nE()},n[y].usd=Xu(n[y].usd+x.usd),n[y].events+=x.events,rE(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 oE(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 rU from"node:fs/promises";import oU from"node:path";fl();gr();var Zu=e=>e.payload?.ts??e.ts,QF=12e4,Ur=e=>Math.round(e*1e4)/1e4,eU=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function tU(e,{since:t}){let{track:n,events:r,brief:o}=e,s=S=>Zu(S)>=t,a=Rn([Xa(n?.name),Xa(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 I=Rn(S.payload.task);I&&i.set(S.boxId,I)}let c=new Map,l=S=>{let I=S||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},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}),d=[...r].sort((S,I)=>Zu(S)-Zu(I)),f=0,p=0,m=0,w={turns:0};for(let S of d){let I=l(S.boxId),$=I.segments[I.segments.length-1],D=Zu(S);switch(I.lastTs!=null&&D>I.lastTs&&s(S)&&($.activeMs+=Math.min(D-I.lastTs,QF)),I.lastTs=D,S.provider&&(I.provider=S.provider),S.type){case"turn.user":{let T=S.payload?.text;if(S.source==="hook"||!String(T||"").trim())break;let A=u(Rn(T));I.segments.push(A),s(S)&&(A.turns+=1);break}case"peer.message":{let T=S.payload?.text;if(!String(T||"").trim())break;let A=u(Rn(T));I.segments.push(A),s(S)&&(A.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let T=S.payload?.tool;T&&($.tools[T]=($.tools[T]||0)+1),s(S)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let{usd:T,tokens:A,model:R,unpriced:E}=Ot(S);E&&(w.turns+=1),$.usd+=T,A&&($.tokens.input+=A.input,$.tokens.output+=A.output,$.tokens.cacheRead+=A.cacheRead,$.tokens.cacheWrite+=A.cacheWrite),$.models[R]=($.models[R]||0)+T,S.provider&&($.provider=S.provider);break}case"handoff.done":s(S)&&(f+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,x=!1;for(let[S,I]of c)for(let $ of I.segments){if(!($.usd>0||$.activeMs>0||$.turns>0||$.toolCalls>0))continue;let T=$.textCat?.category??i.get(S)?.category??Sb($.tools)?.category??a?.category??"uncategorized";y.has(T)||y.set(T,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let A=y.get(T);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 aE(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:S,handoffs:I,merged:$,failed:D,sawCursorActivity:T,unpricedTurns:A}=tU(w,{since:r});T&&(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 S){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,eU());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 oe=U.byTrack.get(y?.id)||{usd:0,activeMs:0};oe.usd+=L.usd,oe.activeMs+=L.activeMs,U.byTrack.set(y?.id,oe)}(R.usd>0||R.activeMs>0||R.turns>0)&&(R.usd=Ur(R.usd),R.dominantCategory=nU(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:sE(y.models),providers:sE(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((x,S)=>S[1].usd-x[1].usd||S[1].activeMs-x[1].activeMs).slice(0,2).map(([x,S])=>({trackId:x,name:p.get(x),usd:Ur(S.usd),activeMs:S.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 nU=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},sE=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 sU(e){let t=String(e||"7d");return t in na?{key:t,ms:na[t]}:{key:"7d",ms:na["7d"]}}function aU(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 iU(e){if(!e?.cwd)return null;try{let n=(await rU.readFile(oU.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 iE(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}:sU(t),d=[];for(let f of e||[]){let p=await F(f),m=aU(f,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),d.push({track:f,events:m,brief:await iU(f)})}return{window:l,...aE(d,{windowMs:u,now:c})}}On();var Hr=36e5,dE=24*Hr,cU=400,lU=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 uU=(e,t)=>e!=null&&t-e<=48*Hr?Hr:dE;function cE(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 dU(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 lE=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),uE=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},fU=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Mo(n)]));async function fE(e,t={}){let n=t.now??Date.now(),r=Mw({...t,now:n}),o=r.since==null?dE:uU(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=lE(),d=0,f=0,p=0,m=0,w=0,y=0,x=new Set,S=new Map,I=[],$=[],D=[],T=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 oe=[];for(let C of U){let Z=C.ts??0;s(Z)?oe.push(C):i(Z)&&(y+=Ot(C).usd)}if(!oe.length)continue;let G=wr(oe,{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);uE(u,G.totalTokens);for(let[C,Z]of Object.entries(G.byModel))c[C]||={usd:0,events:0,tokens:lE()},c[C].usd=Mo(c[C].usd+Z.usd),c[C].events+=Z.events,uE(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 oe){let{usd:Z}=Ot(C);if(!Z)continue;let Y=cE(C.ts??0,o),te=S.get(Y)??{t:Y,total:0,byProvider:{}};te.total+=Z;let we=C.provider||"unknown";te.byProvider[we]=(te.byProvider[we]||0)+Z,S.set(Y,te),(T==null||Y<T)&&(T=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?cE(r.since,o):T,R=[];for(let E=A,L=0;E!=null&&E<r.until&&L<cU;E=dU(E,o),L++){let U=S.get(E);R.push(U?{t:E,total:Mo(U.total),byProvider:fU(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,lU)}}su();var pU=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),mU=20,hU=40;async function pE(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(!pU.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=Dv(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,mU),fixes:c.fixes.slice(0,hU)}}import wU from"node:fs";import mE from"node:fs/promises";import uc from"node:os";import gU from"node:path";Om();nt();var yU=1024*1024,Qu=e=>Math.round(e/yU);async function bU(e){try{let t=await mE.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 kU(){try{return(await Ut()).filter(t=>t.isBostrat).length}catch{return null}}async function xU(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await mE.readdir(gU.join(Xi,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function hE({stateDir:e=Xi,now:t=Date.now}={}){let n=uc.totalmem(),[r,o,s,a]=await Promise.all([fu().catch(()=>null),bU(wU.existsSync(e)?e:uc.tmpdir()),kU(),xU()]),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 vU=864e5;function SU(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 wE(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*vU,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=SU(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 TU from"node:fs";import ot from"node:path";import AU from"node:os";import{execFile as EU}from"node:child_process";import{promisify as _U}from"node:util";var IU=_U(EU),gE=process.env.BOSTRAT_STATE_DIR||ot.join(process.env.HOME||AU.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 $U=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($U.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 yE({stateDir:e=gE,dest:t=process.env.BOSTRAT_SYNC_DIR||`${gE}-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)=>IU("git",["-C",t,...u]);TU.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);d!==Nw&&(Nw=d,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${d}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}Ym();qt();eo();nt();ya();import{execFile as
|
|
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 pc(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=pc(r);[c.primary,c.secondary].forEach((p,m)=>{let w=
|
|
643
|
-
`,{mode:384}),sd.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function mc(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
|
|
633
|
+
_${o} archived_`:"")}async function vF(e,t){let n=await $o();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(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(jr(XA(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Ce(`Starting *${he(u)}* on ${he(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:kF(u,i)}}case"archive":{if(!a.length)return Ce("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Aw(await r.listTracks(),a.join(" "));return i?(jr(gF(e,n,{track:i,responseUrl:t.response_url})),Ce(`Archiving *${he(i.name)}*\u2026`)):Ew(c,a.join(" "))}case"handoff":{let i=r.knownAgents??wF,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:d}=Aw(await r.listTracks(),l);return u?(jr(yF(e,n,{track:u,agent:c,responseUrl:t.response_url})),Ce(`Handing *${he(u.name)}* to ${he(c)}\u2026`)):Ew(d,l)}case"stop":{if(!a.length)return Ce("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Aw(await r.listTracks(),a.join(" "));return i?(jr(bF(e,n,{track:i,responseUrl:t.response_url})),Ce(`Stopping *${he(i.name)}*\u2026`)):Ew(c,a.join(" "))}case"list":return Ce(await xF(r));default:return Ce(hF)}}async function SF(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Rw(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 Zt(),await Co(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${he(l||"question")}~
|
|
634
|
+
\u2713 *${he(a)}*`}}]}).catch(()=>{})}}catch(c){await dt(n.response_url,Ce(`Couldn't answer: ${c?.message||c}`))}}async function TF(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(f=>f.type==="pr.offered"&&f.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===s))throw new Error("offer already answered");let d=Array.isArray(r.actions)?xs.filter(f=>r.actions.includes(f)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(d?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:d}))return;d&&await i.sayToLead(c.id,vs(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await dt(n.response_url,Ce(`Couldn't act on the offer: ${c?.message||c}`))}}async function AF(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 dt(n.response_url,Ce(`Couldn't act on the offer: ${i?.message||i}`))}}async function EF(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await $o();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 dt(t.response_url,{...Ce(`Starting *${he(s)}*\u2026`),replace_original:!0}),jr(XA(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"?jr(SF(e,r,t,o)):n.action_id==="bostrat-ship"?jr(TF(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&jr(AF(e,r,t,o))}async function _F(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 pF(t.channel,t.thread_ts);if(!n)return;let r=await $o();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Lr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function IF(e){return async t=>{if(t.type==="slash_commands")return vF(e,t.payload);if(t.type==="interactive"){await EF(e,t.payload);return}if(t.type==="events_api"){await _F(e,t.payload?.event);return}}}var $F=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 RF(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${he(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:cc(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 OF(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await Co(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${he(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Lr(e,t,r.question||"The lead needs an answer",RF(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Zt()}async function CF(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 Zt(),await Co(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${he(s||"question")}~
|
|
635
|
+
\u2713 _answered_`}}]})}async function MF(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(d=>l[d]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await Lr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${he(cc(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Zt()}async function NF(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Zt();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 Co(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function PF(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 Lr(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${he(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 Zt()}async function DF(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Zt();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Co(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function BF(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??jF)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await KA(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${he(o.name)}*${a}${o.group?` (group \`${he(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${he(a.name)}*`).join(", ");await Lr(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${he(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function jF(e,t){let n=await qn(e);if(!n)return null;let r=zt(n,t);if(!r)return null;let o=await ic.readFile(Nt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function LF(e,t){if(!$F.has(t.type))return;let n=await Rw(t.trackId);if(!n)return;let r=await $o();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 Zt(),await Iw(r,n);return;case"turn.agent":{if(!o.text)return;let s=mF(o.text);if(!s)return;await Lr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:he(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Gu(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return OF(r,n,t);case"prompt.answered":return CF(r,n,t);case"pr.offered":return MF(r,n,t);case"pr.offer.answered":return NF(r,n,t);case"pr.exists.offered":return PF(r,n,t);case"pr.exists.answered":return DF(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 Lr(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 Zt(),await Iw(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Lr(r,n,`Live preview ${o.name}: ${Vu(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${he(o.name)}*: ${Vu(o.slug)}
|
|
636
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return BF(r,n,t,e.deps)}}function ZA(e){let t={deps:e};ht.on("event",s=>{LF(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await $o()||(r&&clearInterval(r),r=null,n=GA({getToken:async()=>(await $o())?.appToken,onEnvelope:IF(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}qt();ue();rn();em();On();import FF from"node:fs/promises";function UF(e){let t=new Map;for(let n of String(e||"").split(`
|
|
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);d!==Nw&&(Nw=d,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${d}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}Ym();qt();eo();nt();ya();import{execFile as MU}from"node:child_process";import{promisify as NU}from"node:util";import{randomUUID as PU}from"node:crypto";import DU from"node:fs";import BU from"node:os";import jU from"node:path";var qr=NU(MU),nd=e=>new Promise(t=>setTimeout(t,e)),Kr=tf,Dw=process.env.BOSTRAT_USAGE_PROBE_CWD||jU.join(BU.homedir(),".bostrat","usage-probe"),LU=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,FU=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,UU=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,xE=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Wr=null,dc=null,fc=null;async function HU(){try{return await qr(W,q("has-session","-t",Kr)),!0}catch{return!1}}function WU(){Wr&&clearTimeout(Wr),Wr=setTimeout(()=>qU().catch(()=>{}),LU),Wr.unref&&Wr.unref()}async function qU(){Wr&&(clearTimeout(Wr),Wr=null);try{await qr(W,q("kill-session","-t",Kr))}catch{}}async function KU(){let e=Date.now()+UU;for(;;){let t=await Bw().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await nd(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await nd(500)}}async function GU(){if(await HU())return;if(!await ir())throw new Error("host tmux server not running \u2014 start globo-tmux.service");DU.mkdirSync(Dw,{recursive:!0}),await Qn(Dw);let e=PU();await qr(W,q("new-session","-d","-s",Kr,"-c",Dw,"-x","200","-y","60",...Na()));let t=`${ef} --session-id ${e}`;await qr(W,q("send-keys","-t",Kr,t,"Enter")),await KU()}async function Bw(){let{stdout:e}=await qr(W,q("capture-pane","-t",Kr,"-p"));return e}async function VU(){await GU(),await qr(W,q("send-keys","-t",Kr,"-l","/usage")),await qr(W,q("send-keys","-t",Kr,"Enter"));let e=Date.now()+FU,t="";for(;await nd(400),t=await Bw(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await nd(900),t=await Bw(),await qr(W,q("send-keys","-t",Kr,"Escape")).catch(()=>{}),WU(),t}function zU(e){let t=String(e||"").split(`
|
|
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 JU(e){let t=String(e||"").split(`
|
|
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 vE({fresh:e=!1}={}){return!e&&dc&&Date.now()-dc.at<xE?dc.data:fc||(fc=(async()=>{try{let t=await VU(),n=zU(t),r={...n,contributors:JU(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return dc={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 dc={at:Date.now()-(xE-3e3),data:n},n}finally{fc=null}})(),fc)}var YU=262144;function XU(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 ZU=new Set([300]),QU=new Set([10080]);function e4(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:ZU.has(t)?"session":QU.has(t)?"week":"other"}function t4(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 n4(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?XU((o-n)/1e3):null,resetsAt:o}}var pc=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function r4(e,t=Date.now()){let n=String(e||"").split(`
|
|
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 pc(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=pc(r);[c.primary,c.secondary].forEach((p,m)=>{let w=n4(p,l,t);if(!w)return;let y=e4(p.window_minutes);y===null&&(y=m===0?"session":"week"),y==="session"&&!u.session?u.session=w:y==="week"&&!u.week?u.week=w:(u.other??=[]).push({label:t4(p.window_minutes),...w})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let f=c.credits;return f&&(f.has_credits||f.unlimited)&&(u.credits={balance:String(f.balance??""),unlimited:!!f.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...pc(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return pc(r)}return pc(r)}async function jw(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Zr(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 gt(n.file,YU),o=r4(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 SE(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 rd({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 jw(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await jw(null))}return n.has("cursor")&&r.push(await rd({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function rd(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?jw(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await vE({fresh:t})}}tr();it();it();function o4(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}var s4=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),od=(e,t)=>Object.assign(new Error(e),{status:t});async function TE(e,t){if(!o4(t))throw od("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${$e(t)}${e}`,{headers:s4(t)})}catch(o){throw od(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw od(r?.error||`plane replied ${n.status}`,n.status);return r}async function AE(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await TE(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function EE(e,{env:t=process.env}={}){if(!e)throw od("message id required",400);return(await TE(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}di();Gl();function Lw(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 a4(e){let[t,n]=e.split("/"),r=Lw(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 _E(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(a4).filter(Boolean)}function i4(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function IE(e){let t=_E(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:i4,allowed(r){if(n)return!0;let o=Lw((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function $E(e,t){let n=_E(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Lw(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import RE from"node:crypto";import sd from"node:fs";import c4 from"node:path";function OE(e=process.env,{stateDir:t=Xi,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=c4.join(t,"api-token");try{let a=sd.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=RE.randomBytes(32).toString("base64url");try{sd.mkdirSync(t,{recursive:!0}),sd.writeFileSync(o,s+`
|
|
643
|
+
`,{mode:384}),sd.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function mc(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 CE(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:RE.timingSafeEqual(n,r)}function ME(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 l4=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],u4=e=>l4.some(t=>t.test(e));function NE({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=ME(o);return CE(c,e)?a():u4(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||mc(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 PE(e,t,{enforce:n=!0}={}){return!n||CE(ME(e),t)?!0:mc(e)}rn();uu();uu();import d4 from"node:http";import f4 from"node:net";function ad(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 id(e){let t=ad(e.headers.host);return t?{slug:t,view:Em(t)}:null}var p4=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),m4=e=>{let t=p4(e.name);return`<!doctype html><meta charset="utf-8">
|
|
644
644
|
<meta http-equiv="refresh" content="3">
|
|
645
645
|
<title>${t} \u2014 starting</title>
|
|
646
646
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
647
647
|
<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
|
|
648
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function DE(e,t,n){let r=id(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(m4(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=d4.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 BE(e,t,n){let r=id(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
649
|
\r
|
|
650
650
|
bostrat: no live view "${s}"\r
|
|
651
|
-
`),t.destroy(),!0;let a=
|
|
651
|
+
`),t.destroy(),!0;let a=f4.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
652
|
`)+`\r
|
|
653
653
|
\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 DE(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}.${f4(n.host)}/`}function f4(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 BE from"node:os";import{randomUUID as Uw}from"node:crypto";var p4=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||BE.hostname(),nodeName:e.BOSTRAT_NODE_NAME||BE.hostname()});var jE=32768,LE=120,FE=1e3,UE=new Set(["claude","codex","cursor"]),m4=new Set(["off","transition","nag"]),h4=[[0,59],[0,23],[1,31],[1,12],[0,6]];function w4(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=h4[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>LE)throw new Error(`name is too long (${t.length}); cap is ${LE}`);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(!w4(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>jE)throw new Error(`prompt is too long (${t.length} chars); cap is ${jE}`);return t},expectation(e){let t=Qt(e)||null;if(t&&t.length>FE)throw new Error(`expectation is too long (${t.length}); cap is ${FE}`);return t},notify(e){let t=Qt(e)||"off";if(!m4.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(!UE.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...UE].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 g4(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}=p4();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function WE(e,t){let n=g4(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 qE(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"],y4=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function KE(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 GE(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 b4(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"?y4(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 VE(e,t,n){let r=KE(t),{patch:o,changed:s}=b4(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 HE=600;async function zE(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>HE)throw new Error(`reason is too long (${r.length}); cap is ${HE}`);let o=KE(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 JE=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 YE(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 JE(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 XE(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 JE(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 ZE=10,QE=200,k4=120,x4=24,t_=/^\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 v4=(e=Date.now())=>new Date(e).toISOString().slice(0,10),st=(e,t="i")=>new RegExp(e,t),S4=[{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"}],T4=[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 A4({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of S4)if(o.test(n)||o.test(r))return s;return null}function e_(e){let t=String(e||"");for(let n of T4){let r=n.exec(t);if(r)return r[0].trim()}return null}function n_(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 E4=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function _4(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,k4)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${x4}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=E4.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=e_(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>QE)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${QE}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=e_(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>ZE)throw new Error(`${r.length} bullets \u2014 over the ${ZE}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function I4(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:A4({title:r.title,headRef:r.headRef})}))}async function r_(e,t){let n=await I4(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function o_(e,{md:t,day:n=null}){let r=_4(t),o=n==null||n===""?v4():String(n);if(!t_.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 s_(e,{day:t}){let n=String(t||"");if(!t_.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 $4}from"node:child_process";import{promisify as R4}from"node:util";var qw=R4($4),a_=3e4,O4=30*6e4,C4=a_,M4=10*6e4,N4=10080*6e4,P4=60*6e4,D4=8,B4=1440*6e4,j4=3,i_="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Ww=60*6e4,L4=15*6e4,F4=9e4,U4=1440*6e4,cd=new Map,ld=new Map,H4=/could not resolve|not found|no pull requests?/i,W4=e=>H4.test(String(e?.message||e||""));function q4(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Ww)}var c_={fast:C4,slow:M4,decay:P4};function l_(e,t){return e.armedAt&&t-e.armedAt<O4||q4(e,t)||u_(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>N4?"decay":"slow"}function K4(e,t){return c_[l_(e,t)]}function wc(e,t,n,r){let o=e.lanes?.get(ne(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 G4(e,t){Bn("watch");try{let{stdout:n}=await qw("gh",["pr","view",String(t),"--repo",e,"--json",i_],{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 V4(e){Bn("watch");try{let{stdout:t}=await qw("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",i_],{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 z4(){let e=new Map;for(let t of await ie().catch(()=>[]))e.set(t.id,t);return e}async function J4(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 Y4(e,t){let n=await ho(e);return n?_s(n,t):null}async function X4(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 Z4(e,t){let n=await ho(e);if(!n)throw new Error(`no local checkout for ${e}`);return mi(n,t)}async function Q4(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 e2=10*6e4;async function t2(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>B4){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<e2)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 d_(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 n2(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 u_(e,t,{grace:n=F4}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=U4}async function r2(e,t,n,r){let o=r.now??Date.now();if(!u_(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 o2(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<L4;await mx(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await px(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 s2(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 a2(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 d_(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 i2(e={}){let{now:t=Date.now(),auth:n=Wp,view:r=G4,list:o=V4,links:s=gx,trackMap:a=z4,emitMerged:i=J4,mark:c=vr,archiveIfArmed:l=Ln,notifyIfArmed:u=Fn,notifyIfResolved:d=Iu,batchThreshold:f=j4,detail:p=Y4,merge:m=X4,updateBranch:w=Z4,lookup:y=Qe,disarm:x=ui,emitDisarmed:S=Q4,localHasCommit:I=n2,claim:$=Nn,midTurn:D=wo}=e;if(!(await n().catch(()=>null))?.authenticated||Wl()>0)return 0;let A=await s().catch(()=>[]),R=Ux(A),E=A.filter(Y=>t-(cd.get(ne(Y.repo,Y.number))||0)>=K4(Y,t));if(!E.length)return 0;let L=Date.now(),U=new Map,oe=new Map;for(let Y of[...E].sort(hi)){let te=l_(Y,t);U.set(ne(Y.repo,Y.number),{lane:te,intervalMs:c_[te]}),cd.set(ne(Y.repo,Y.number),t),oe.has(Y.repo)||oe.set(Y.repo,[]),oe.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:S,localHasCommit:I,claim:$,midTurn:D};for(let[Y,te]of oe){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){W4(ge)&&ce.add(ne(V.repo,V.number))}}if(we)for(let V of te){let ge=ne(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>=D4&&(await a2(V,G,C),ld.delete(ge),cd.delete(ge))}continue}if(ld.delete(ge),B.state==="MERGED"||B.state==="CLOSED")B.state==="MERGED"?await d_(V,B,G,C):await s2(V,B,G,C),cd.delete(ge);else{await r2(V,B,G,C).catch(()=>{}),await o2(V,B,G,C).catch(()=>{});let ye=!V.trackId||R.get(V.trackId)===ge;V.autoMerge&&ye&&await t2(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 f_(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await i2()}catch{}finally{e=!1}}},a_)}ue();Pn();tr();_r();Cn();function p_(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function c2(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var l2=new Set(["merged","closed","lost"]);function u2(e,t){if(!e?.blocked)return null;let n=p_(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 d2(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function f2(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 p2(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 m_(e,t={}){let{now:n=Date.now(),slugOf:r=xe,lookup:o=Qe,note:s=hx,tracks:a=async()=>(await ie()).filter(d=>!qe(d)),asked:i=f2,emitChange:c=p2}=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(!c2(p))continue;let m=await o(f,p.number).catch(()=>null);if(!m?.trackId||l2.has(m.state))continue;let{prev:w}=await s(f,p.number,{blocked:p_(p),sha:p.headSha??null,at:n});if(!w)continue;let y=u2(w,p);if(!y||d2(m,y.change))continue;u??=new Map((await a()).map($=>[$.id,$]));let x=u.get(m.trackId);if(!x)continue;let S=p.headSha??null;if(await i(x,y.change,p.number,S,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:S,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 m2}from"node:crypto";var h2=2e4,h_=3,ia=new Map,Kw=new Set;async function w_(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 w2(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===h_&&(Kw.add(d.name),await w_(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:m2(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(ia.get(d.name)||0)>=h_;if(ia.delete(d.name),p||!Kw.has(d.name)){Kw.add(d.name);let m=await w_(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 g_(){return gc?()=>{}:(gc=setInterval(()=>{Gw||(Gw=!0,w2().catch(()=>{}).finally(()=>{Gw=!1}))},h2),gc.unref?.(),()=>{clearInterval(gc),gc=null})}Ss();$m();ue();$s();Cs();Ss();import{randomUUID as g2}from"node:crypto";async function y2(){let e=await Promise.resolve().then(()=>(_r(),WT));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Vw={listRepoPrs:cn,readEvents:F,appendEvent:O,sayDeps:y2,currentBranch:Rs};async function b2(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 b2(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=g2();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,rx(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 T_ from"node:os";import y_ from"node:fs/promises";import ud from"node:path";import{execFile as k2}from"node:child_process";import{promisify as x2}from"node:util";import{fileURLToPath as v2}from"node:url";var S2=x2(k2),k_=ud.dirname(v2(import.meta.url)),T2=ud.resolve(k_,"..","..","client","dist"),A2="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",E2=new Map([...A2].map((e,t)=>[e,t]));function _2(e){let t=[],n=0,r=0;for(let o of e){let s=E2.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 I2(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=_2(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 $2(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 b_=new Map;async function R2(e){let t;try{t=await y_.stat(e)}catch{return null}let n=b_.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await y_.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:I2(r.mappings||"")};return b_.set(e,o),o}catch{return null}}var Yw=null;function O2(){return Yw||(Yw=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):S2("git",["rev-parse","--short","HEAD"],{cwd:ud.resolve(k_,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Yw}var C2=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function M2(e,{distDir:t=T2}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(C2)){let[a,i,c,l]=s,u=await R2(ud.join(t,"assets",`${i}.map`)),d=u?$2(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 x_(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await O2();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await M2(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var N2=()=>process.env.BOSTRAT_STATE_DIR||ca.join(process.env.HOME||T_.tmpdir(),".bostrat"),Gr=()=>ca.join(N2(),"telemetry"),A_=()=>ca.join(Gr(),".relay-offsets.json"),P2=50,D2=14,B2=3e4,v_=500,It=(e,t)=>typeof e=="string"?e.slice(0,t):"";function j2(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 E_=e=>new Date(e).toISOString().slice(0,10),S_=0;async function __(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,P2):[]).map(s=>j2(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await x_(s);await Zn.mkdir(Gr(),{recursive:!0});let o=ca.join(Gr(),`${E_(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+=v_){let p=d.slice(f,f+v_).map(w=>F2(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 R_(r)}return{sent:o,disabled:!1}}var Xw=null,Zw=!1;function O_(){Xw||(Xw=setInterval(async()=>{if(!Zw){Zw=!0;try{await U2()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Zw=!1}}},B2),Xw.unref?.())}import pd from"node:fs/promises";import D_ from"node:path";import H2 from"node:os";var B_=()=>process.env.BOSTRAT_STATE_DIR||D_.join(process.env.HOME||H2.tmpdir(),".bostrat"),j_=()=>D_.join(B_(),"dictation-glossary.json"),W2=100,q2=500,K2=["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 C_(){let e={},t=Date.now();for(let n of K2)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function L_(){return md||(Qw??=pd.readFile(j_(),"utf8").then(e=>JSON.parse(e)?.terms??C_()).catch(()=>C_()).then(e=>md=e).finally(()=>Qw=null),Qw)}async function M_(){let e=JSON.stringify({terms:md},null,2),t=j_(),n=`${t}.${process.pid}.tmp`;await pd.mkdir(B_(),{recursive:!0}),await pd.writeFile(n,e),await pd.rename(n,t)}function G2(){return eg=eg.then(M_,M_),eg.catch(()=>{})}async function tg(){return Object.values(await L_()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var V2=e=>Math.max(1,Math.ceil(e.length/4));async function z2(){let e=[],t=0;for(let{term:n}of await tg()){let r=V2(n);if(!(t+r>q2)&&(e.push(n),t+=r,e.length>=W2))break}return e}async function J2(e,t){let n=await L_(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var N_=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),fd=e=>e.toLowerCase();function P_(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function F_(e,t){let n=N_(e),r=N_(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++:(P_(r[l])&&i.add(r[l]),l++);for(;l<s;l++)P_(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await J2(u,"learned");return await G2(),[...i]}var yc=null;function Y2(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 U_(){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 H_(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 z2())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=Y2(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 X2(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 Z2(e,t={}){let n=ng[X2(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 Q2(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 eH(){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 tH(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=Q2(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 nH={openai:tH};async function W_(){let e=await Ku();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await eH()}:{configured:!1}}async function q_(){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 nH[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function K_(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=Z2(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 Z_=ke.dirname(oH(import.meta.url)),G_=Number(process.env.PORT)||3362,wd=Xh(process.env),Q_=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,la=EE(process.env.BOSTRAT_ALLOW_IPS),ig=_E(process.env.BOSTRAT_VIEW_FRONT_CIDRS,id),eI=$E(),tI=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(NE);v.use(CE({token:eI,enforce:tI}));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:Q_,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 bk(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});v.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=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 V_=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(),lH=rA({kind:jt}),kc=null,ft=()=>kc&&["auto","prompt","off"].includes(kc)?kc:lH,ag=null,uH=ec.has(jt),nI=async({to:e,from:t})=>!uH||ju(await tc())?!1:(xc.arm({to:e,from:t,kind:jt}),!0);async function dH(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 nI(ag)}}var rI=(()=>{try{return cH(import.meta.url)("../package.json").version||null}catch{return null}})(),xc=sA({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:rI,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 oI(){try{let e=await oA();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:rI,installKind:jt,policy:ft()})}catch{}}oI();setInterval(oI,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>V_){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,V_).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 $A()})});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(oE(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 iE(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 fE(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 xE(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 XA(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 QA(n.track,n.box)).join(`
|
|
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-A_>6*3600*1e3&&(A_=t,H2(t).catch(()=>{})),r.length}async function H2(e=Date.now()){let t=I_(e-L2*24*3600*1e3),n=[];try{n=await Zn.readdir(Gr())}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 Zn.rm(ca.join(Gr(),s),{force:!0});let o=await O_();for(let s of r)delete o[s];await C_(o)}var dd=new Map;function R_(e,t=Date.now()){let n=e||"unknown",r=dd.get(n);return r||(dd.size>512&&dd.clear(),r={tokens:40,at:t},dd.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 O_(){try{return JSON.parse(await Zn.readFile(__(),"utf8"))||{}}catch{return{}}}async function C_(e){await Zn.mkdir(Gr(),{recursive:!0}),await Zn.writeFile(__(),JSON.stringify(e),"utf8")}function W2(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||E_.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 q2({fetchImpl:e=fetch}={}){let t=await gA();if(!t)return{sent:0,disabled:!0};let n;try{n=(await Zn.readdir(Gr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await O_(),o=0;for(let s of n){let a=ca.join(Gr(),s),i=r[s]||0,c;try{let f=await Zn.open(a,"r");try{let p=(await f.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await f.read(c,0,c.length,i)}finally{await f.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
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(`
|
|
658
658
|
`)+`
|
|
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;eE(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 re(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 tE(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 pE(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(!I_(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:Ox()})});v.post("/api/github/refresh",(e,t)=>{Rg(),t.json({ok:!0})});v.get("/api/gmail/search",async(e,t)=>{try{let n=await SE(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 TE(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 re(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 sI({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 fH(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 jx();await fH(n.repos).catch(()=>{}),await m_(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 S=await gd({repo:n,number:y,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 mi(n,y),await St({repo:x,number:y,trackId:S.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:S,...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 Wx(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:Hx()})});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 re(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:Lx(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 Fx(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 re(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 re(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 re(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 sI({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 qx(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
|
|
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
660
|
${x.log??"(no fetchable log \u2014 see link)"}
|
|
661
661
|
`).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 ux(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 sI({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 dx(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 re(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 re(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:DE(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 Jv(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 re(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 Yv(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 Xv(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 Zv(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var pH=[["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 pH){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 mA(o),i=n?await re(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 re(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 aI=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 aI(o,n,r),l=await aA({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 aI(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 lA(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 uA(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 iA(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 re(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 re(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 RA(),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 re(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 NA(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 PA(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 re(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await DA(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 re(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 jA(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 BA()})}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 re(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await LA(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 re(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 hS({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 wS(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 bS(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 BS(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await kS(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 CS({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 MS(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 re(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 NS({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 PS(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 DS(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 KS({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=WS(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 GS(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 re(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 WE(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var iI=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await re(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=GE(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 iI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await VE(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 iI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await zE(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var cI=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 re(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await YE(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(cI(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 re(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await XE(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(cI(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 re(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await qE(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 re(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 VS({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 zS(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 JS(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await YS(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=n_(e.query.since||null);t.json({since:n,repos:Hw(),...await r_(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var lI=async({trackId:e,cwd:t})=>{let n=e?await re(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 lI({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 o_(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 lI({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 s_(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var mH=120,og=400,ua=null,z_=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
|
|
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 oe(a):null;if(!l&&s&&(l=(await ie()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+ke.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),d=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),f={title:n?d(n,gH):l.name,body:d(i,og),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await O(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:f}),t.json({ok:!0,notification:f,delivered:ua?ua.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 q_(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 nc(),terms:await tg(),problem:await W_()})});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 H_(n,r)})});v.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await oe(e.params.id))return t.status(404).json({error:"track not found"});t.json(await K_())});v.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});if(n.archivedAt)return t.status(409).json({error:"track is archived"});t.json(await G_())}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 oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=e.body||{};t.json({ok:!0,...await V_(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 _u({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),d=null;if(c)try{await ea(u,c,{source:"create"})}catch(p){d=p?.message||String(p)}let f=null;if(l)try{await Fu(u,await Xs(l),{source:"picker"})}catch(p){f=p?.message||String(p)}t.json({ok:!0,track:u,...d?{ticketWarning:d}:{},...f?{circuitWarning:f}:{}})}catch(u){let d=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(d)?400:500).json({error:d})}});v.get("/api/tracks/:id",async(e,t)=>{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:fI(Ys(e.params.id)),subtasks:Eo(e.params.id).map(Tc),pendingOffers:await wh(n)})});v.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Oh(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await kw(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 Ao(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 Nh(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 Mh(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 Su(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 oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),t.flushHeaders();let r=i=>t.write(`id: ${i.seq}
|
|
664
664
|
data: ${JSON.stringify(i)}
|
|
665
665
|
|
|
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=
|
|
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=Fg(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
667
667
|
|
|
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 Bt(e.params.id,
|
|
668
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});v.get("/api/tracks/:id/search",async(e,t)=>{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=Za(await F(n)),a=pl(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 ry(),harvest:Qg()})});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 oy(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 yH=["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(`
|
|
669
|
+
`);v.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Bt(e.params.id,yH)})}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 $u(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 Bt(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 oe(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(n.archivedAt)return t.status(409).json({error:"track is archived"});if(!n.cwd)return t.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(e.body)?e.body:null,o=await oT(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 oe(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(!n.cwd)return t.status(404).json({error:"track has no workspace"});let r=ke.basename(String(e.params.file||""));if(!r||!qm(ke.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=Wm(n.cwd),s,a;try{s=await at.promises.realpath(o),a=await at.promises.realpath(ke.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+ke.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 bH=/^[0-9a-fA-F-]{8,64}$/;v.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});let{box:r,uuid:o}=e.params,s=Number(e.params.idx);if(!bH.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(f=>f.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=Wt(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=aH.createInterface({input:at.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let p;try{p=JSON.parse(f)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let d=l?gf(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",d.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(d.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await Eu(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 bh(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 Tu(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 $h({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 yh(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 Th(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 oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=e.query.fresh==="1"||e.query.fresh==="true";t.json(await rd(r,{fresh:o}))});v.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Ah(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 Vh(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Tc(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:Eo(e.params.id).map(Tc)})});v.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await gh(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:Tc(Mu(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:Tc(zh(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Tc(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 Ru(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 vh(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(fI(Ys(e.params.id))??{running:!1})});v.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Uh(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=Fh(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 Ou(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 qh({...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 fI(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:Gc(),paths:by,trace:await yy({limit:n})})});var sg=ke.resolve(eI,"..","..","client","dist");at.existsSync(sg)&&(v.use(Lt.static(sg)),v.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(ke.join(sg,"index.html"),Sc)));var pI=cH.createServer(v),kH=new uH({noServer:!0}),mI=(e,t,n)=>{if(!(mc(e)||la.allowed(la.clientIp(e))||ig.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
670
670
|
\r
|
|
671
|
-
`),t.destroy();return}if(
|
|
671
|
+
`),t.destroy();return}if(BE(e,t,n))return;if(!PE(e,nI,{enforce:rI})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
672
672
|
\r
|
|
673
|
-
`),t.destroy();return}let r=
|
|
673
|
+
`),t.destroy();return}let r=jy(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
674
674
|
\r
|
|
675
675
|
`),t.destroy();return}let o=r.sessionName;if(!Ne(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
676
676
|
\r
|
|
677
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
677
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;kH.handleUpgrade(e,t,n,i=>Py(i,o,{cols:s,rows:a}))};pI.on("upgrade",mI);var hI=process.env.BOSTRAT_CERT_DIR||ke.join(process.env.HOME||hd.tmpdir(),".bostrat","certs"),X_=Number(process.env.BOSTRAT_TLS_PORT)||3443,Z_=ke.join(hI,"bostrat.globopi.key"),Q_=ke.join(hI,"bostrat.globopi.crt");if(at.existsSync(Z_)&&at.existsSync(Q_)){let e=lH.createServer({key:at.readFileSync(Z_),cert:at.readFileSync(Q_)},v);e.on("upgrade",mI),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(X_,wd,()=>{console.log(`[bostrat-agent] https listening on ${wd||"0.0.0.0"}:${X_}`)})}pI.listen(z_,wd,()=>{console.log(`[bostrat-agent] listening on ${wd||"0.0.0.0"}:${z_}`);let e=GT(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: ${xt}`),console.log(`[bostrat-agent] auto-trigger default: ${tI}%`),console.log(`[bostrat-agent] ip allowlist: ${la.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(ig.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Lh().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Kh().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Ph().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(()=>Dh()).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)),Ih().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)),IH(),SH(),TH(),_H(),M0({listTracks:ie,getTrack:oe}).then(t=>console.log(`[bostrat-agent] spend caps ${t.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(t.spent).length} provider counter(s)`)).catch(t=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",t?.message||t)),lT()&&uT({listTracks:ie}),xy().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)),m_(),b_(),HA({resolveTrack:oe}),ZA({listTracks:ie,createTrack:_u,archiveTrack:Ao,listRepos:En,resolveTrack:oe,sayToLead:Bt,answerPrompt:Tu,startLeadHandoff:Ru,interruptLead:Eu,listSubtaskJobs:Eo,cancelSubtask:Mu,readEvents:F,appendEvent:O,divertShip:zw,answerPrExists:Jw,knownAgents:Wi}),xH(),Da().then(t=>{t||console.warn(`[boot] no host tmux server on ${xt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),M_(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(ua=nA({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||hd.hostname(),name:process.env.BOSTRAT_NODE_NAME||hd.hostname(),listTracks:ie,detectAgents:vu,sampleMetrics:gE,onTracksTransferred:Wh,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||hd.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(()=>{}),Y_=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(()=>{}),Y_=null},onUpdateAvailable:async t=>{try{ag={latest:t.latest,current:t.current},await bn({...t,policy:ft()}),ft()==="auto"&&await oI({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>mH(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await bn({current:t,latest:a,min:n,installKind:o,required:!0,policy:ft(),status:"available",reason:"refused"}),ft()!=="off"&&a){let i=await Lu({to:a,kind:jt,reason:"hard-floor"});i.ok?(xc.cancel(),await bn({current:t,latest:a,min:n,installKind:o,required:!0,policy:ft(),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:ft}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),zo().catch(()=>{}),tT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),WA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),AS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),HS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),kx().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{ua?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function xH(){vH(),cu().catch(()=>{}),setInterval(()=>cu().catch(()=>{}),1e4)}function vH(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=ke.resolve(eI,"..","bin",e),n=ke.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!at.existsSync(t))continue;at.mkdirSync(n,{recursive:!0});let r=ke.join(n,e);(at.existsSync(r)?at.readlinkSync(r):null)!==t&&(at.rmSync(r,{force:!0}),at.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 SH(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ie(),r=await kE({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 TH(){let e=null,t=async()=>{try{let n=ls(),r=0,o=[],s=null;try{let l=await ie(),u=await Cw(l);r=u.fleet.unpricedTurns||0,o=u.fleet.unpricedModels||[],s=await yE(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
678
|
`);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
|
|
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 AH=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,EH=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function _H(){let e=async()=>{try{let n=await ny();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await ie()).filter(a=>!qe(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await ty([...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,AH).unref()},EH).unref()}function IH(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ie();for(let n of t)qe(n)||!n.boxes?.some(r=>r.state==="live")||(await Su(n.id).catch(()=>{}),await rS(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|