bostrat-node 1.9.2 → 1.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +56 -56
- package/dist/index.js +200 -196
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
1
|
+
var AO=Object.defineProperty;var q=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Ap=(e,t)=>{for(var n in t)AO(e,n,{get:t[n],enumerable:!0})};import{createHash as EO,randomBytes as _O,timingSafeEqual as IO}from"node:crypto";function Xy(){return _O(32).toString("hex")}function La(e){let t=typeof e=="string"?e.trim():"";return t?EO("sha256").update(t).digest("hex"):null}function Zy(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:IO(Buffer.from(e),Buffer.from(t))}function Fa(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Ep.set(e,(Ep.get(e)||0)+1),_p.has(e)&&n-_p.get(e)<$O)return!1;_p.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=${Ep.get(e)}${o?` ${o}`:""}`),!0}async function Qy(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 Un,Hn,Ep,_p,$O,ds=q(()=>{Un="BOSTRAT_HOOK_TOKEN",Hn="x-bostrat-hook";Ep=new Map,_p=new Map,$O=6e4});import $t from"node:fs/promises";import eb from"node:os";import At from"node:path";import{execFile as RO}from"node:child_process";import{promisify as OO}from"node:util";function al(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function CO(){return process.env.CLAUDE_CONFIG_DIR?At.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):At.join(eb.homedir(),".claude.json")}function MO(){return process.env.CLAUDE_CONFIG_DIR?At.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):At.join(eb.homedir(),".claude","settings.json")}async function yr(e){if(!e||typeof e!="string")return;let t=At.resolve(e),n=CO(),r=MO(),o={};try{o=JSON.parse(await $t.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[t]=o.projects[t]||{};let s=o.projects[t];Array.isArray(s.allowedTools)||(s.allowedTools=[]),s.hasTrustDialogAccepted=!0,s.hasCompletedProjectOnboarding=!0;let a=process.env.ANTHROPIC_API_KEY;if(a){let c=a.slice(-20);o.customApiKeyResponses=o.customApiKeyResponses||{},o.customApiKeyResponses.approved=o.customApiKeyResponses.approved||[],o.customApiKeyResponses.rejected=o.customApiKeyResponses.rejected||[],o.customApiKeyResponses.approved.includes(c)||o.customApiKeyResponses.approved.push(c)}await $t.mkdir(At.dirname(n),{recursive:!0}),await $t.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await $t.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.remoteControlAtStartup=!1,i.tui===void 0&&(i.tui="default"),process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' -H "${Hn}: $${Un}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await $t.mkdir(At.dirname(r),{recursive:!0}),await $t.writeFile(r,JSON.stringify(i,null,2)),await NO(t)}async function NO(e){try{let t=At.resolve(e),n=At.join(t,".bostrat","memory","claude"),r=At.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await $t.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(At.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await $t.mkdir(n,{recursive:!0}),await $t.mkdir(At.dirname(r),{recursive:!0}),await $t.writeFile(r,JSON.stringify(o,null,2)),await PO(t,["/.claude/settings.local.json"])}catch{}}async function PO(e,t){try{let{stdout:n}=await OO(RO)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=At.resolve(e,n.trim()),o=await $t.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
2
|
`).includes(a));if(!s.length)return;await $t.mkdir(At.dirname(r),{recursive:!0}),await $t.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var Ua=q(()=>{ds()});import Ip from"node:fs/promises";import DO from"node:os";import il from"node:path";function jO(){let e=process.env.CODEX_HOME||il.join(DO.homedir(),".codex");return il.join(e,"config.toml")}function tb(e){return`[projects."${e}"]`}function BO(e,t){let n=tb(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 ps(e){if(!e||typeof e!="string")return;let t=il.resolve(e),n=jO(),r="";try{r=await Ip.readFile(n,"utf8")}catch{}if(BO(r,t))return;let o=tb(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
|
|
14
|
-
`,"utf8")}var
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await Ip.mkdir(il.dirname(n),{recursive:!0}),await Ip.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var cl=q(()=>{});import nb from"node:fs/promises";import LO from"node:os";import Ha from"node:path";function FO(e){let n=Ha.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ha.join(LO.homedir(),".cursor");return Ha.join(r,"projects",n)}async function fs(e){if(!e||typeof e!="string")return;let t=Ha.resolve(e),n=FO(t),r=Ha.join(n,".workspace-trusted");await nb.mkdir(n,{recursive:!0}),await nb.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var ll=q(()=>{});import Rp from"node:fs/promises";import br from"node:path";import{execFile as UO}from"node:child_process";import{promisify as HO}from"node:util";async function VO(e){try{return(await Rp.stat(br.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function JO(e){let t=i=>Rp.readFile(br.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 Wn(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await VO(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:br.basename(o),branch:await JO(o)});return}if(s>=GO)return;let i;try{i=await Rp.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||zO.has(c.name)||await t(br.join(o,c.name),s+1)}for(let o of KO)await t(br.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await _e(o.path),a=s?.toLowerCase();if(a){if(n.has(a))continue;n.add(a),o.slug=s}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function YO(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function _e(e){if(!e)return null;if($p.has(e))return $p.get(e);let t=null;try{let{stdout:n}=await WO("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=YO(n)}catch{t=null}return $p.set(e,t),t}async function ms(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await Wn())if((await _e(n.path))?.toLowerCase()===t)return n.path;return null}var WO,rb,qO,KO,GO,zO,$p,kr=q(()=>{WO=HO(UO),rb=process.env.HOME||"/home/globo",qO=br.join(process.env.BOSTRAT_STATE_DIR||br.join(rb,".bostrat"),"repos"),KO=[...(process.env.BOSTRAT_REPOS_ROOT||br.join(rb,"Documents")).split(","),qO].map(e=>e.trim()).filter(Boolean),GO=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,zO=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);$p=new Map});import XO from"node:fs";import ob from"node:fs/promises";import Wa from"node:path";import ZO from"node:os";import{fileURLToPath as QO}from"node:url";import{execFile as eC}from"node:child_process";import{promisify as tC}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 ul(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function dl(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!ab.test(n))throw hs("repo must be owner/name",400);let r=Op.get(n);if(r&&r.expiresAt-Date.now()>oC)return r;if(!ul(t))throw hs("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await ib(`${$e(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:cb(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw hs(`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 hs(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 Op.set(n,a),a}async function qa({env:e=process.env,force:t=!1}={}){if(!ul(e))return{configured:!1,repos:[],username:null};if(!t&&ho&&Date.now()-ho.at<sC)return ho.data;try{let n=await ib(`${$e(e)}/v1/github/repos`,{headers:cb(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return ho={at:Date.now(),data:r},r}catch(n){return ho?ho.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function lb(){Op.clear(),ho=null}async function yn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await _e(e):null);if(!r)return{};let{token:o}=await dl(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Cp({env:e=process.env}={}){return(await qa({env:e})).username??null}function lC(e){let[t,n]=String(e).split("/");return Wa.join(rC,t,n)}async function Mp(e,{env:t=process.env}={}){let n=(...r)=>sb("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${iC()}" ${cC(t)}`),await n("config","credential.useHttpPath","true")}async function pl(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!ab.test(String(e||"")))throw hs(`bad repo slug "${e}"`,400);let r=lC(e);if(XO.existsSync(Wa.join(r,".git")))return r;let{token:o}=await dl(e,{env:t});await ob.mkdir(Wa.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await sb("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:aC,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await ob.rm(r,{recursive:!0,force:!0}).catch(()=>{}),hs(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Mp(r,{env:t}),r}var sb,nC,rC,ab,oC,sC,aC,ib,Op,ho,cb,hs,iC,cC,pt=q(()=>{kr();sb=tC(eC),nC=process.env.BOSTRAT_STATE_DIR||Wa.join(process.env.HOME||ZO.tmpdir(),".bostrat"),rC=Wa.join(nC,"repos"),ab=/^[^/\s]+\/[^/\s]+$/,oC=5*6e4,sC=6e4,aC=10*6e4,ib=fetch,Op=new Map,ho=null;cb=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),hs=(e,t)=>Object.assign(new Error(e),{status:t});iC=()=>QO(new URL("../bin/bostrat-git-credential",import.meta.url)),cC=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function db(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function fb(e,t,n,r=ub){let o=$e(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await dC(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function mb(e,{env:t=process.env,force:n=!1,timeoutMs:r=ub}={}){if(!db(t))return{facts:[],source:"unconfigured"};let o=Np.get(e);if(!n&&o&&Date.now()-o.at<uC)return{facts:o.facts,source:"cache"};try{let s=await fb(`/v1/memory/${encodeURIComponent(e)}`,{headers:pb(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 Np.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 fl(e,t,{env:n=process.env}={}){if(!db(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await fb(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...pb(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 Np.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 uC,ub,Np,dC,pb,hb=q(()=>{pt();uC=6e4,ub=5e3,Np=new Map,dC=(...e)=>fetch(...e);pb=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Pp from"node:fs/promises";import ws from"node:path";import pC from"node:os";import{EventEmitter as fC}from"node:events";import{randomUUID as mC}from"node:crypto";function gs(e){return e?.cwd?ws.join(e.cwd,".bostrat","events"):ws.join(Dp,"events")}async function yC(e,t){if(!ml.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);ml.set(t,o)}let n=ml.get(t)+1;return ml.set(t,n),n}function jp(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 C(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!hC.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!wC.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!gC.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?ws.join(Dp,"events"):gs(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=jp(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await yC(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||mC(),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 gb(c)&&(c=yb(c),l=JSON.parse(c)),await Pp.mkdir(r,{recursive:!0}),await Pp.appendFile(wb(r,n),c+`
|
|
15
|
+
`),ot.emit("event",l),ot.emit(`track:${n}`,l),l}async function K(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?ws.join(Dp,"events"):gs(e));let o;try{o=await Pp.readFile(wb(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(gb(a)?yb(a):a))}catch{}return s}function bb(e,t){let n=`track:${e}`;return ot.on(n,t),()=>ot.off(n,t)}var hC,wC,gC,ot,Dp,wb,ml,bC,gb,yb,de=q(()=>{hC=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","delivery.queued","delivery.settled","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","memory.harvested","handoff.artifact","code.pushed","code.committed","code.rework","rework.cause.offered","rework.cause.answered","rework.cause.retracted","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","job.stalled","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","loop.create.offered","loop.create.answered","loop.created","loop.update.offered","loop.update.answered","loop.updated","loop.delete.offered","loop.delete.answered","loop.deleted","loop.paused","followup.armed","followup.armed.cancelled","followup.offered","followup.answered","followup.create.offered","followup.create.answered","followup.created","planning.change.offered","planning.change.answered","planning.changed","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.phase.changed","track.operation.changed","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),wC=new Set(["user","agent","orchestrator","system","peer"]),gC=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),ot=new fC;ot.setMaxListeners(0);Dp=process.env.BOSTRAT_STATE_DIR||ws.join(process.env.HOME||pC.tmpdir(),".bostrat");wb=(e,t)=>ws.join(e,`${t}.ndjson`),ml=new Map;bC=/(?<!\\)((?:\\\\)*)\\u0000/g,gb=e=>e.includes("\\u0000"),yb=e=>e.replace(bC,"$1\\ufffd")});import ne from"node:fs/promises";import kC from"node:os";import be from"node:path";import{execFile as xC}from"node:child_process";import{promisify as vC}from"node:util";function wl(){return be.join(gl,"memory","repos")}function wo(e){return e===qn?be.join(gl,"memory",qn):be.join(wl(),e)}async function Ga(e){try{return JSON.parse(await ne.readFile(be.join(e,Eb),"utf8"))}catch{return{}}}async function Lp(e,t){let n=await Ga(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await ne.mkdir(e,{recursive:!0}),await ne.writeFile(be.join(e,Eb),JSON.stringify(n))}function xb(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 vb(e){try{let{stdout:t}=await SC("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Fp(e){if(!e)return[];let t=xb(await vb(e));if(t)return[t];let n=[],r=[];try{r=await ne.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=xb(await vb(be.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function yl(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 xr(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function bn(e,t=null){let n=t??await ne.readFile(e,"utf8").catch(()=>""),{fm:r}=yl(n),o=xr(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await ne.stat(e)).mtimeMs}catch{return 0}}function _b(e,t){let n=xr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===qn){let o=xr(e,"trust");if(!o||/^operator/i.test(o))return qn}return t}function Up(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
|
-
`)){let l=
|
|
18
|
+
`)){let l=bs(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=bs(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 Ib(e,t){let n=xr(t,"name")||e.replace(/\.md$/,""),r=xr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function za(e){let t=[];try{t=await ne.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 $b(e,t){let n=be.join(e,".bostrat","memory"),r=await za(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Sb.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let p=be.join(n,d);if(o)try{if((await ne.stat(p)).mtimeMs<o-AC)continue}catch{continue}let f;try{f=await ne.readFile(p,"utf8")}catch{continue}let{fm:w}=yl(f),m=_b(w,t);if(!m)continue;let g=wo(m),b=be.join(g,d),x=await bn(p,f);if(l.has(g)||l.set(g,await Ga(g)),(l.get(g)[d]||0)>=x)continue;let y=await bn(b).catch(()=>0);if(await ne.access(b).then(()=>!0).catch(()=>!1)&&y>=x)continue;await ne.mkdir(g,{recursive:!0}),await ne.writeFile(b,f),c++;let O=a.get(g);O||a.set(g,O={won:new Set,generated:new Map}),O.won.add(d),O.generated.set(d,Ib(d,w)),i.has(m)||i.set(m,new Set),i.get(m).add(d)}let u=await ne.readFile(be.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,p]of a){let f=be.join(d,"MEMORY.md"),w=await ne.readFile(f,"utf8").catch(()=>"");await ne.writeFile(f,Up(w,u,p.won,p.generated))}return Sb.set(e,s),{harvested:c,changedByKey:i}}async function Rb(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await ne.access(be.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await Fp(r),a=await $b(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 EC(){let e=be.join(gl,"workspaces"),t=[];try{t=await ne.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return Rb(t.filter(n=>n.isDirectory()).map(n=>be.join(e,n.name)))}async function $C(e,t){let n=wo(e),r=await Ga(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!_C.test(l)||l==="MEMORY.md")continue;let u=be.join(n,l),d=await ne.access(u).then(()=>!0).catch(()=>!1),p=d?await bn(u):0,f=Number(c.modified)||0;if(c.deleted){f>p&&(d&&await ne.rm(u,{force:!0}),await Lp(n,[[l,f]]),d&&(a.push(l),i++));continue}d&&p>=f||(r[l]||0)>=f||(await ne.mkdir(n,{recursive:!0}),await ne.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=be.join(n,"MEMORY.md"),l=await ne.readFile(c,"utf8").catch(()=>""),u=Up(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
|
-
`).filter(p=>!d.has(
|
|
24
|
-
`)}await
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
23
|
+
`).filter(p=>!d.has(bs(p))).join(`
|
|
24
|
+
`)}await ne.mkdir(n,{recursive:!0}),await ne.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function bl(e,t=null){let n=wo(e),r=t?.length?t:await za(n);if(!r.length)return{pushed:0,source:"empty"};let o=IC(await ne.readFile(be.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=be.join(n,a),c;try{c=await ne.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await bn(i,c),indexLine:o.get(a)??null})}return fl(e,s)}async function RC(e,{timeoutMs:t}={}){let{facts:n,source:r}=await mb(e,t?{timeoutMs:t}:{});n.length?await $C(e,n):r==="plane"&&await bl(e)}function OC(e){let t=e.split(`
|
|
25
|
+
`);t.length>kb&&(t=t.slice(0,kb));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>TC&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function MC(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=be.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=wo(u),p=await Ga(d);for(let[g,b]of Object.entries(p)){let x=be.join(n,g);await ne.access(x).then(()=>!0).catch(()=>!1)&&await bn(x)<=b&&(await ne.rm(x,{force:!0}),o++)}let f=await za(d);if(!f.length)continue;await ne.mkdir(n,{recursive:!0});for(let g of f){let b=be.join(d,g),x=be.join(n,g),y=await bn(b),$=await bn(x).catch(()=>0);await ne.access(x).then(()=>!0).catch(()=>!1)&&$>=y||(await ne.copyFile(b,x),r++)}let w=await ne.readFile(be.join(d,"MEMORY.md"),"utf8").catch(()=>""),m=u===qn?"## operator memory":`## repo: ${u}`;w.trim()&&s.push(t.length>1?`${m}
|
|
28
28
|
|
|
29
|
-
${
|
|
29
|
+
${w.trim()}`:w.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
|
|
30
30
|
|
|
31
31
|
`)+`
|
|
32
|
-
`,c=
|
|
33
|
-
`).filter(f=>!s.has(
|
|
34
|
-
`);await
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=OC(i);await ne.mkdir(n,{recursive:!0}),await ne.writeFile(be.join(n,"MEMORY.md"),c);let l=be.join(n,"claude");return await ne.mkdir(l,{recursive:!0}),await ne.writeFile(be.join(l,"MEMORY.md"),CC,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Ob(e){try{let t=await Fp(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,qn];return await Promise.all(n.map(r=>RC(r,{timeoutMs:NC}))),await MC(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Cb(){return{...et,pending:ys.size,queued:Bp.size}}function Mb(e){if(hl.has(e))return hl.get(e);Bp.add(e);let t=Tb.then(async()=>{Bp.delete(e),hl.delete(e);let n=await Wp(e);return et.runs++,et.lastRunAt=Date.now(),n.error?(et.errors++,et.lastError=n.error):et.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return hl.set(e,t),Tb=t.then(()=>{}),t}function kl(e,{delayMs:t=PC}={}){if(!e||typeof e!="string")return;et.scheduled++;let n=ys.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{ys.delete(e),Mb(e)},t);r.unref?.(),ys.set(e,r)}async function Hp(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await ne.access(be.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=ys.get(e);r&&(clearTimeout(r),ys.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([Mb(e),s])}finally{clearTimeout(o)}}async function Nb(e){return Ka||(Ka=(async()=>{try{Ab||(Ab=!0,await jC());let{harvested:t,changedByKey:n}=await Rb(e);for(let[r,o]of n)await bl(r,[...o]);return et.reconciles++,et.lastReconcileAt=Date.now(),et.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return et.errors++,et.lastError=t?.message||String(t),{harvested:0,error:et.lastError}}finally{Ka=null}})(),Ka)}function DC(){return be.join(gl,"memory",".full-sweep-done")}async function Pb(){let e=DC();try{let t=JSON.parse(await ne.readFile(e,"utf8"));return et.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...et.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await EC();for(let[o,s]of n)await bl(o,[...s]);let r={at:Date.now(),harvested:t};return await ne.mkdir(be.dirname(e),{recursive:!0}),await ne.writeFile(e,JSON.stringify(r)),et.backfill=r,{ran:!0,...r}}catch(t){return et.errors++,et.lastError=t?.message||String(t),{ran:!1,error:et.lastError}}}async function jC(){let e=0,t=[];try{t=await ne.readdir(wl(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=be.join(wl(),n.name),o=wo(qn),s=new Set,a=new Map;for(let f of await za(r)){let w=be.join(r,f),m;try{m=await ne.readFile(w,"utf8")}catch{continue}let{fm:g}=yl(m);if(_b(g,n.name)!==qn)continue;let b=be.join(o,f),x=await bn(b).catch(()=>0),y=await bn(w,m);await ne.mkdir(o,{recursive:!0}),y>x&&await ne.writeFile(b,m),await ne.rm(w,{force:!0}),s.add(f),a.set(f,Ib(f,g)),e++}if(!s.size)continue;let i=Date.now();await Lp(r,[...s].map(f=>[f,i])),await fl(n.name,[...s].map(f=>({file:f,deleted:!0,modified:i})));let c=be.join(r,"MEMORY.md"),l=await ne.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(f=>!s.has(bs(f))).join(`
|
|
34
|
+
`);await ne.writeFile(c,u).catch(()=>{});let d=be.join(o,"MEMORY.md"),p=await ne.readFile(d,"utf8").catch(()=>"");await ne.writeFile(d,Up(p,l,s,a))}return{moved:e}}async function Db(){let e=[],t=[qn];try{for(let n of await ne.readdir(wl(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=wo(n),o=await za(r),s=await Ga(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await ne.readFile(be.join(r,i),"utf8").catch(()=>""),{fm:l}=yl(c);a.push({file:i,name:xr(l,"name")||i.replace(/\.md$/,""),description:xr(l,"description")||"",scope:xr(l,"scope")||null,modified:await bn(be.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function jb(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=wo(e),r=be.join(n,t);if(!await ne.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await ne.rm(r,{force:!0}),await Lp(n,[[t,s]]);let a=be.join(n,"MEMORY.md"),i=await ne.readFile(a,"utf8").catch(()=>"");await ne.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>bs(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await fl(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Wp(e){try{let t=await Fp(e),{harvested:n,changedByKey:r}=await $b(e,t[0]||null);for(let[o,s]of r)await bl(o,[...s]);return n>0&&await LC(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function LC(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,BC)}}await C({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var SC,gl,kb,TC,qn,Eb,bs,Sb,AC,_C,IC,CC,NC,PC,ys,Bp,Tb,et,hl,Ab,Ka,BC,Va=q(()=>{hb();de();SC=vC(xC),gl=process.env.BOSTRAT_STATE_DIR||be.join(process.env.HOME||kC.tmpdir(),".bostrat"),kb=200,TC=25*1024;qn="operator";Eb=".tombstones.json";bs=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Sb=new Map,AC=2e3;_C=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,IC=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=bs(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};CC=`# Track memory
|
|
38
38
|
|
|
39
39
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
40
40
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
41
41
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
42
|
-
`;
|
|
43
|
-
`,n!=null?{mode:n}:void 0),await
|
|
44
|
-
`,"utf8");let n=(await
|
|
45
|
-
`).filter(Boolean);n.length>
|
|
42
|
+
`;NC=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;PC=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,ys=new Map,Bp=new Set,Tb=Promise.resolve(),et={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};hl=new Map;Ab=!1,Ka=null;BC=20});import xl from"node:fs/promises";import FC from"node:path";async function Et(e,t,{mode:n}={}){await xl.mkdir(FC.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++UC}.tmp`;try{await xl.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await xl.rename(r,e)}catch(o){throw await xl.unlink(r).catch(()=>{}),o}}var UC,vr=q(()=>{UC=0});import Hb from"node:fs/promises";import Wb from"node:os";import qb from"node:path";function Fe(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Xa(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),HC);try{return await VC(`${$e(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function Gb(){return Sl||(qp||=Hb.readFile(Kb(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Sl={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>qp=null),qp)}async function Tl(){await Et(Kb(),Sl).catch(()=>{})}function Fb(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Gp(e){let t=await Gb(),n=Fb(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=>Fb(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>Bb&&(t.ops=t.ops.slice(-Bb)),await Tl()}function Ub(){vl||(vl=setTimeout(()=>{vl=null,ks().catch(()=>{})},JC),vl.unref?.())}function Kp(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function XC(e,t=process.env){if(e.op==="record"){let n=await Xa(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Ya(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:zC()}]})},t);if(!n.ok)throw Kp(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 Hb.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Xa(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Ya(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Kp(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 Xa(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Ya(t)},t);if(!n.ok)throw Kp(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function ks({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Fe(e)?Ja||(Ja=(async()=>{let r=await Gb();ZC(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await XC(a,e)}catch(c){if(!c?.refused)return Ub(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<qC)return Ub(),await Tl(),{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>Lb&&(r.parked=r.parked.slice(-Lb)),s++,await Tl();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await Tl()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Ja=null}),Ja):{flushed:0,source:"unconfigured"}}function ZC(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=YC);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 yt(e,t,{trackId:n=null,env:r=process.env}={}){return Fe(r)?(await Gp({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),ks({env:r})):{queued:!1,source:"unconfigured"}}async function Sr(e,t,n,r,o,{env:s=process.env}={}){return Fe(s)?(await Gp({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),ks({env:s})):{queued:!1,source:"unconfigured"}}async function Zt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Fe(r)?(await Gp({op:"delete",family:e,id:t,deletedAt:n}),ks({env:r})):{queued:!1,source:"unconfigured"}}async function st(e,{env:t=process.env}={}){if(!Fe(t))return{records:[],source:"unconfigured"};try{let n=await Xa(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Ya(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 Za(e,t,n,r,{env:o=process.env}={}){if(!Fe(o))return null;try{let s=await Xa(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ya(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 HC,WC,Bb,qC,Lb,KC,GC,Kb,zC,VC,JC,YC,Ya,Sl,qp,vl,Ja,Tr=q(()=>{pt();vr();HC=1e4,WC=3e4,Bb=2e3,qC=5,Lb=100,KC=60*6e4,GC=()=>process.env.BOSTRAT_STATE_DIR||qb.join(process.env.HOME||Wb.tmpdir(),".bostrat"),Kb=()=>qb.join(GC(),"content-outbox.json"),zC=()=>process.env.BOSTRAT_NODE_ID||Wb.hostname(),VC=(...e)=>fetch(...e),JC=WC,YC=KC;Ya=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Sl=null,qp=null,vl=null,Ja=null});import zp from"node:fs/promises";import zb from"node:path";async function Vb(e,t,{box:n=null,agent:r=null}={}){if(!e||!Fe())return{pushed:!1};try{let o=Date.now();return await yt("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await Sr("checkpoints",e,o,"md",t),Qa=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function rM(){if(Qa&&Date.now()-Qa.at<nM)return{records:Qa.records,source:"plane"};let e=await st("checkpoints");return e.source==="plane"&&(Qa={at:Date.now(),records:e.records}),e}async function Al(e){try{let t=tM(e);if(!t||!Fe())return{restored:!1};let n=zb.join(e,QC,eM);if(await zp.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await rM();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 Za("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await zp.mkdir(zb.dirname(n),{recursive:!0}),await zp.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var QC,eM,tM,nM,Qa,El=q(()=>{Tr();QC=".bostrat/handoff",eM="latest.md",tM=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;nM=6e4,Qa=null});import ei from"node:fs/promises";import oM from"node:os";import Yb from"node:path";function go(e){let t=String(e??"");for(let n of sM)t=t.replace(n,"\u2039redacted\u203A");return t}function _l(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function aM(e){try{await ei.mkdir(Vp,{recursive:!0}),await ei.appendFile(xs,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await ei.readFile(xs,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>Jb&&await ei.writeFile(xs,`${n.slice(-Jb).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=
|
|
49
|
-
`,r=
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${xs}: ${t?.message||t}`)}}async function le(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&&_l())try{t.paneTail=go((await je(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(_l()||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 aM(t)}async function Xb({limit:e=80}={}){try{return(await ei.readFile(xs,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=go(o.paneTail)),o}catch{return{raw:go(r)}}})}catch{return[]}}var sM,Vp,xs,Jb,Zb,kn=q(()=>{ct();sM=[/[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];Vp=process.env.BOSTRAT_DEBUG_DIR||Yb.join(oM.homedir(),".bostrat","debug"),xs=Yb.join(Vp,"handoff.jsonl"),Jb=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;Zb={DEBUG_DIR:Vp,LOG_FILE:xs}});import Il from"node:fs/promises";import Jp from"node:path";import iM from"node:os";async function ek(e=Qb){await Il.mkdir(e,{recursive:!0});for(let t of lM){let n=t==="prepare-commit-msg"?uM:t==="post-commit"?dM:`${Yp}exit 0
|
|
49
|
+
`,r=Jp.join(e,t),o=null;try{o=await Il.readFile(r,"utf8")}catch{}o!==n&&await Il.writeFile(r,n,{mode:493}),await Il.chmod(r,493).catch(()=>{})}return e}function tk({nodeUrl:e,dir:t=Qb,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",`${ti}=${e}`]}function nk(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var ti,cM,Qb,lM,Yp,uM,dM,Xp=q(()=>{ds();ti="BOSTRAT_NODE_URL",cM=process.env.BOSTRAT_STATE_DIR||Jp.join(process.env.HOME||iM.tmpdir(),".bostrat"),Qb=Jp.join(cM,"git-hooks"),lM=["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"],Yp=`#!/bin/sh
|
|
50
50
|
# bostrat git hook shim (agent/src/git-hooks.js) \u2014 chains to the repo's own hook,
|
|
51
51
|
# then stamps/reports for rework attribution when running inside a bostrat box.
|
|
52
52
|
hook=$(basename "$0")
|
|
@@ -61,73 +61,75 @@ esac
|
|
|
61
61
|
if [ -x "$orig/$hook" ] && [ "$orig/$hook" != "$0" ]; then
|
|
62
62
|
"$orig/$hook" "$@" || exit $?
|
|
63
63
|
fi
|
|
64
|
-
`,
|
|
64
|
+
`,uM=`${Yp}
|
|
65
65
|
# \u2500\u2500 bostrat: append provenance trailers (Layer 0). Skipped for merge commits
|
|
66
66
|
# (nobody "wrote" a merge) and outside a box (no token / no node url).
|
|
67
|
-
[ -z "$${
|
|
68
|
-
[ -z "$${
|
|
67
|
+
[ -z "$${Un}" ] && exit 0
|
|
68
|
+
[ -z "$${ti}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${Hn}: $${Un}" "$${ti}/api/git/trailers" 2>/dev/null) || exit 0
|
|
71
71
|
[ -z "$trailers" ] && exit 0
|
|
72
72
|
printf '%s\\n' "$trailers" | while IFS= read -r line; do
|
|
73
73
|
[ -n "$line" ] && git interpret-trailers --in-place --if-exists addIfDifferent --trailer "$line" "$1" >/dev/null 2>&1 || true
|
|
74
74
|
done
|
|
75
75
|
exit 0
|
|
76
|
-
`,
|
|
76
|
+
`,dM=`${Yp}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
|
-
[ -z "$${
|
|
79
|
-
[ -z "$${
|
|
78
|
+
[ -z "$${Un}" ] && exit 0
|
|
79
|
+
[ -z "$${ti}" ] && exit 0
|
|
80
80
|
sha=$(git rev-parse HEAD 2>/dev/null) || exit 0
|
|
81
81
|
cwd=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
82
|
-
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${
|
|
83
|
-
-H 'content-type: application/json' -H "${
|
|
82
|
+
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${ti}/api/git/committed" \\
|
|
83
|
+
-H 'content-type: application/json' -H "${Hn}: $${Un}" --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,p,f,
|
|
87
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
88
|
-
`).
|
|
89
|
-
`)
|
|
90
|
-
`)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
`)
|
|
94
|
-
`)
|
|
95
|
-
`)){let
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
`).trim()
|
|
99
|
-
`).trim():""
|
|
100
|
-
`)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
`);
|
|
104
|
-
`);for(let r
|
|
105
|
-
`);
|
|
106
|
-
`)
|
|
107
|
-
`).
|
|
108
|
-
`)
|
|
109
|
-
`)
|
|
110
|
-
`),o=
|
|
111
|
-
`),
|
|
112
|
-
`),
|
|
113
|
-
`)}function
|
|
114
|
-
`)
|
|
115
|
-
`)
|
|
116
|
-
`)
|
|
85
|
+
`});import{execFile as ok}from"node:child_process";import{promisify as pM}from"node:util";import Rl from"node:fs";import{randomUUID as sk}from"node:crypto";function Ts(e){return new Promise(t=>setTimeout(t,e))}function $l(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 ak(e){return`/tmp/tmux-${e}/default`}function fM({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=ak(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function ri(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${ni}=1`]}function oi(e,t,n){let r=yo(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&&Be(n)&&(s+=` --name ${n}`),s}function yo(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 wM(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:yo(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function ik(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await ck(e,{loginShell:t});return r===Ol?!0:r===vs?!1:n}async function ck(e,{loginShell:t=!1,timeoutMs:n=Zp}={}){if(!e)return vs;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Re(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),Ol}catch(s){return s?.killed||s?.signal?Ss:s?.code===1||s?.code===127?vs:Ss}}async function Cl(e,{retries:t=gM,sleep:n=yM,probe:r=ck}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Zp});for(let a=0;s===Ss&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Zp*2});return s}function Z(...e){return["-S",Rt,...e]}async function Er(){if(!Rl.existsSync(Rt))return!1;try{return await Re(X,Z("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function si(){if(await Er())return!0;try{Rl.mkdirSync(Rt.replace(/\/[^/]+$/,""),{recursive:!0}),await Re(X,Z("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Er()}function Be(e){return typeof e=="string"&&bM.test(e)}async function Qt(){if(!Rl.existsSync(Rt))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 Re(X,Z("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),m=Number(i),g=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!hM.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ke(e){return(await Qt()).find(n=>n.name===e)||null}function vM(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=xM&&kM.test(t)}function Ml(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function lk(e){try{let{stdout:t}=await Re(X,Z("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function AM(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[],r=s=>/[A-Za-z0-9_]/.test(s);for(let s=1;s<=t.length-3;s++)r(t[s-1])===r(t[s])&&n.push(t.slice(s));if(!n.length)return null;let o=n.map(s=>s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${o.join("|")})`)}async function EM(e,t){let n=Date.now()+ai(),r=AM(t),o=!1,s=0,a=0;for(;;){if(await Ts(SM),s+=1,!await lk(e))return;let i=await je(e,{lines:15}),c=!o&&TM.test(i)&&!!r?.test(i),l=!o&&!!r?.test(i);if(!c&&!l&&s>1&&!await Kn(e)){if(a+=1,a>1)return}else c||(a=0);if(c){o=!0,await le({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Ar(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Re(X,Z("send-keys","-t",e,"C-u")),await Re(X,Z("send-keys","-t",e,"-l",t)),await Re(X,Z("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await le({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 _r(e,t=mM,n={}){if(!vM(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=Ml(r);if(!Be(o))throw new Error("resulting tmux session name is invalid");if(!await si())throw new Error(`host tmux server not running on ${Rt} and auto-start failed \u2014 start one: ${X} -S ${Rt} new-session -d -s keeper "sleep 2147483647"`);if(await lk(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Qp,a=n.agent||Ar(s),i=!!n.resumeSessionId,c=n.resumeSessionId||sk(),l=i?s:oi(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&yo(s),p=n.firstRun===!0;p||(d&&await yr(t),a==="codex"&&await ps(t),a==="cursor"&&await fs(t),await Ob(t),n.freshWorkspace!==!0&&await Al(t));let f=d&&!p?al():null,w=Xy(),m=["-e",`${Un}=${w}`];m.push(...tk({nodeUrl:nk()})),f&&m.push("-e",`ANTHROPIC_API_KEY=${f}`),d&&m.push(...ri());try{await Re(X,Z("new-session","-d","-s",o,"-c",t,...m))}catch(g){throw $l(f?$l(g,f):g,w)}return await bo(o),await Re(X,Z("set-option","-t",o,"@bostrat-session","1")),await Re(X,Z("set-option","-t",o,"@bostrat-title",r)),await Re(X,Z("set-option","-t",o,"@bostrat-sid",c)),await Re(X,Z("set-option","-t",o,"@bostrat-agent",a)),await Re(X,Z("send-keys","-t",o,l,"Enter")),wM(l)&&EM(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:w}}async function en(e){if(!Be(e))throw new Error("invalid session name");await Re(X,Z("kill-session","-t",e))}async function uk(e,t){if(Be(e))try{let n=t?Z("set-option","-t",e,"@bostrat-sid",t):Z("set-option","-u","-t",e,"@bostrat-sid");await Re(X,n)}catch{}}async function je(e,{lines:t=40}={}){if(!Be(e))return"";try{let n=Z("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Re(X,n);return String(r||"")}catch{return""}}async function dk(e){if(!Be(e))return[];try{let{stdout:t}=await Re(X,Z("list-panes","-s","-t",e,"-F","#{pane_pid}"));return String(t||"").trim().split(`
|
|
88
|
+
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function pk(e){if(!Be(e))return 0;try{let{stdout:t}=await Re(X,Z("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
89
|
+
`).filter(Boolean).length}catch{return 0}}async function bo(e,{cols:t=$M,rows:n=RM}={}){if(!Be(e))return!0;if(await pk(e))return!1;try{let r=await fk(e);if(r.cols===t&&r.rows===n)return!0;await Re(X,Z("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Re(X,Z("set-window-option","-t",e,"window-size","latest")),await Ts(80)}catch{}return!0}async function OM(e){if(!Be(e))return 0;try{let{stdout:t}=await Re(X,Z("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Kn(e){let t=await OM(e);if(!t)return!1;try{return await Re("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function fk(e){if(!Be(e))return{cols:0,rows:0};try{let{stdout:t}=await Re(X,Z("display-message","-t",e,"-p","#{window_width} #{window_height}")),[n,r]=String(t||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function Nl(e,{cols:t=_M,rows:n=IM}={}){if(Be(e)&&!await pk(e))try{let r=await fk(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Re(X,Z("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Re(X,Z("set-window-option","-t",e,"window-size","latest")),await Ts(80))}catch{}}async function Gn(e){return await Nl(e),je(e,{lines:tt})}async function Ke(e,t,{settleMs:n=40,kind:r}={}){if(!Be(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>CM.has(s));for(let s=0;s<o.length;s++)await Re(X,Z("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Ts(n);return le({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function As(e,t,{kind:n}={}){if(!Be(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await Re(X,Z("send-keys","-t",e,"-N",String(r),"BSpace")),le({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Dl(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Be(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await Re(X,Z("send-keys","-t",e,"-l",s)),n>0&&await Ts(n);le({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function MM(e,t){return new Promise((n,r)=>{let o=ok(X,Z(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function NM(e,t){let n=`bostrat-${sk().slice(0,8)}`;await MM(["load-buffer","-b",n,"-"],t),await Re(X,Z("paste-buffer","-p","-d","-b",n,"-t",e))}async function nt(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!Be(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await NM(e,t):await Re(X,Z("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Ts(a),await Re(X,Z("send-keys","-t",e,"Enter"))}le({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 Re,X,rk,Rt,mM,Qp,ni,ef,hM,Ol,vs,Ss,Zp,gM,yM,tf,bM,kM,xM,ai,SM,TM,_M,IM,tt,$M,RM,CM,Pl,ct=q(()=>{Ua();cl();ll();Va();El();kn();ds();Xp();Re=pM(ok);X=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";rk=typeof process.getuid=="function"?process.getuid():0,Rt=fM({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Rl.existsSync(ak(rk))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:rk}),mM=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Qp=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",ni="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";ef="bostrat-usage-probe",hM=new Set([ef]);Ol="present",vs="absent",Ss="unknown",Zp=5e3,gM=[250,1e3,3e3],yM=e=>new Promise(t=>setTimeout(t,e));tf={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"};bM=/^[A-Za-z0-9_.-]{1,64}$/;kM=/^[A-Za-z0-9 ._↻-]+$/,xM=64;ai=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,SM=1e3,TM=/not found|Unknown command/i;_M=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,IM=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,tt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,$M=Number(process.env.BOSTRAT_BOX_COLS)||120,RM=Number(process.env.BOSTRAT_BOX_ROWS)||40;CM=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);Pl=1600});import ii from"node:fs";import FM from"node:fs/promises";import Es from"node:path";import UM from"node:os";function of(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function sf(e){let t=Es.join(rf,of(e)),n;try{n=ii.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}=ii.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function tn(e,t){if(!t)return null;let n=Es.join(rf,of(e),`${t}.jsonl`);return ii.existsSync(n)?n:null}function kk(e){if(!e)return[];let t=Es.join(rf,of(e)),n;try{n=ii.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=ii.statSync(Es.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function _t(e,t=131072){let n=await FM.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 WM(e){let t=e.split(`
|
|
90
|
+
`);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 ci(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=tn(e,t),o=!0,!r)return null}else if(n){if(r=sf(e),o=!1,!r)return null}else return null;let s;try{s=WM(await _t(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(bk,s.model),i=bk[s.model]||HM;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 bk,HM,rf,nn=q(()=>{bk={"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},HM=2e5,rf=process.env.BOSTRAT_CLAUDE_PROJECTS||Es.join(UM.homedir(),".claude","projects")});function qM(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===`
|
|
91
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function li(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 ui(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 Ne(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(Sk(o)){t.has(o)&&r++;continue}n.push(o)}return n}function Tk(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 zM(e){let t=Tk(e),n=ft(Ne(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${Ge(n)}`:`Read lines ${t.from}\u2013${t.to} of ${Ge(n)}`:e.includes("-i")&&n?`Edit ${Ge(n)}`:null}function af(e){if(e.includes("--files")){let s=ft(Ne(e,xk)[0]);return s&&s!=="."?`List files in ${_s(s)}`:"List files"}let t=Ne(e,xk),n=t[0];if(!n)return null;let r=ft(t.slice(1).find(s=>s!==".")),o=Is(n,40);return r?`Search for \u201C${o}\u201D in ${_s(r)}`:`Search for \u201C${o}\u201D`}function cf(e){let t=Ne(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 lf(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=Ne(e)[0];return n?`Run ${Ge(n)}`:null}function VM(e){let t=Ne(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 vk(e){return e.find(t=>/^https?:\/\//.test(t))||null}function XM(e){let t=ui(li(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Ge(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return uf(t[2]);let n=Ge(t[0]);if(YM.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=JM[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function uf(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
92
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=qM(t);for(let i=0;i<r.length-1;i++){let c=ui(li(r[i])),l=ui(li(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Ge(c[0]))&&Ge(l[0])==="sed"){let u=Tk(l),d=Ne(c)[0];if(u&&d)return Is(u.from===u.to?`Read line ${u.from} of ${Ge(d)}`:`Read lines ${u.from}\u2013${u.to} of ${Ge(d)}`,88)}}let o=[];for(let i of r){let c=XM(i);c&&o.push({d:c,head:Ge(ui(li(i))[0]||""),sub:Ne(ui(li(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":Is(s?s.d:o[0].d,88)}var Is,ft,Ge,_s,Sk,xk,KM,GM,JM,YM,Ak=q(()=>{Is=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,ft=e=>e&&!/[$`]/.test(e)?e:null,Ge=e=>e.replace(/\/+$/,"").split("/").pop()||e,_s=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},Sk=e=>e.startsWith("-"),xk=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);KM={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 ${Ge(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"},GM={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"};JM={git:e=>{let t=Ne(e,new Set(["-C","-c"])),n=KM[t[0]];return n?n(t,e):null},gh:e=>{let t=Ne(e),n=GM[t[0]];return n?n(t):null},sed:zM,rg:af,grep:af,egrep:af,nl:e=>{let t=ft(Ne(e)[0]);return t?`Read ${Ge(t)}`:null},cat:e=>{let t=ft(Ne(e)[0]);return t?`Read ${Ge(t)}`:null},head:e=>{let t=ft(Ne(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Ge(t)}`:null},tail:e=>{let t=ft(Ne(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Ge(t)}`:null},wc:e=>{let t=ft(Ne(e)[0]);return t?`Count lines in ${Ge(t)}`:"Count input lines"},ls:e=>{let t=ft(Ne(e)[0]);return t&&t!=="."?`List files in ${_s(t)}`:"List files"},find:e=>ft(e[1])&&!Sk(e[1])&&e[1]!=="."?`Find files in ${_s(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=ft(Ne(e)[0]);return t?`Create directory ${_s(t)}`:null},touch:e=>{let t=ft(Ne(e)[0]);return t?`Create ${Ge(t)}`:null},rm:e=>{let t=ft(Ne(e)[0]);return t?`Delete ${_s(t)}`:null},mv:e=>{let t=ft(Ne(e)[0]);return t?`Move ${Ge(t)}`:null},cp:e=>{let t=ft(Ne(e)[0]);return t?`Copy ${Ge(t)}`:null},chmod:e=>{let t=ft(Ne(e)[1]);return t?`Change permissions on ${Ge(t)}`:null},diff:e=>{let t=Ne(e);return t.length>=2?`Compare ${Ge(t[0])} and ${Ge(t[1])}`:null},curl:e=>{let t=vk(e);return t?`Fetch ${Is(t,56)}`:"Fetch a URL"},wget:e=>{let t=vk(e);return t?`Fetch ${Is(t,56)}`:"Fetch a URL"},npm:cf,pnpm:cf,yarn:cf,npx:e=>{let t=Ne(e)[0];return t?`Run ${t}`:null},node:e=>lf(e,"Node"),python:e=>lf(e,"Python"),python3:e=>lf(e,"Python"),make:e=>{let t=Ne(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:VM,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=Ne(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=Ne(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Ne(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},YM=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function $s(e){let t=e.split(`
|
|
93
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Ir(e,t=""){let n=t.trim();return n||uf(e)||$s(e)}function df(e,t){if(e){if(Array.isArray(e)){for(let n of e)df(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))df(n,t)}}}async function Ek(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await _t(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
94
|
+
`)){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(df(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Ir(d.command,d.description),ts:u})}return o.slice(-n)}function _k(e,t){let n=a=>a?.box??null,r=new Map;for(let a of t||[]){if(a.role!=="assistant"||!(a.text||"").trim())continue;let i=n(a);r.has(i)||r.set(i,[]),r.get(i).push(a)}let o=new Map;for(let a of e||[]){let i=n(a);o.has(i)||o.set(i,[]),o.get(i).push(a)}let s=[];for(let[a,i]of o)s.push(...ZM(i,r.get(a)||[]));return s.sort((a,i)=>(a.ts||0)-(i.ts||0))}function ZM(e,t){let n=[...t].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((w,m)=>m>=s&&(w.ts||0)>(l.ts||0)),d=u===-1?r.length:u,p=r.slice(s,d);if(s=d,!p.length)continue;let f=p.map(w=>({...w,n:i(w)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:f,ts:Math.max(...f.map(w=>w.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,Rs=q(()=>{nn();Ak();pf=new Set(["Bash"])});import Ik from"node:fs";import mf from"node:fs/promises";import ff from"node:path";import QM from"node:os";async function $k(e){try{return await mf.realpath(e)}catch{return e}}function tN(e=eN){let t=[],n=r=>{let o;try{o=Ik.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=ff.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:Ik.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function nN(e,t=1048576){let n;try{n=await mf.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(`
|
|
95
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function rN(e){let t=await nN(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 ko(e,t){if(!e&&!t)return null;let n=tN();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await $k(e);for(let o of n){let s=await rN(o.file);if(s&&(s===e||await $k(s)===r))return o}return null}async function Rk(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await ko(e,t);if(!o)return[];let s;try{s=await _t(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let p of s.split(`
|
|
96
|
+
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if((w.type==="turn_context"&&w.payload?.model||w.type==="session_meta"&&w.payload?.model)&&(i=w.payload.model),w.type==="event_msg"){let x=w.payload?.type;x==="task_started"?(l=!0,u=!1,d=w.payload?.turn_id||null):x==="task_complete"&&l&&(l=!1,!u&&w.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:xo,sourceId:w.payload?.turn_id||d||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i}));continue}if(w.type!=="response_item")continue;let m=w.payload;if(!m)continue;if((m.type==="function_call"||m.type==="custom_tool_call")&&m.name){u=!0,hf.has(m.name)||c.push(m.name);continue}if(m.type!=="message"||m.role!=="assistant")continue;u=!0;let g=sN(m.content),b=c.length?c.slice():null;c=[],!(!g&&!b)&&a.push({role:"assistant",text:g,tools:b||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i})}if(i)for(let p of a)p.model||(p.model=i);return a.slice(-n)}function aN(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 iN(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+=`
|
|
97
|
+
`;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 cN(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=iN(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Ok(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await ko(e,t);if(!o)return[];let s;try{s=await _t(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
98
|
+
`)){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"&&di.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=aN(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=cN(u.input));if(!d.length)continue;let p=u.call_id||u.id||null;d.forEach((f,w)=>{a.push({id:p?w?`${p}:${w}`:p:null,tool:u.name,command:f,description:"",summary:Ir(f),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Ck(e,t){let n=await ko(e,t);if(!n)return null;try{return Math.round((await mf.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var eN,oN,sN,di,hf,xo,vo=q(()=>{nn();Rs();eN=process.env.BOSTRAT_CODEX_SESSIONS||ff.join(process.env.CODEX_HOME||ff.join(QM.homedir(),".codex"),"sessions");oN=new Set(["output_text","text"]),sN=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&oN.has(t.type)).map(t=>t.text).join(`
|
|
99
|
+
`).trim():"";di=new Set(["exec_command","shell","exec"]),hf=new Set(["wait","write_stdin"]),xo="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 pi({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Mk.has(r))return Mk.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 Nk(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||pi({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===xo?"other":pi({text:t}),code:null,status:null,requestId:null}}return null}function Pk(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:pi({text:n}),code:null,status:lN(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function lN(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var JG,Mk,Bl=q(()=>{vo();JG=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Mk=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 uN(e,t){return!e||!t?null:tn(e,t)}function dN(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(`
|
|
100
|
+
`).trim():""}function pN(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 fN(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function wf(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 Dk(e,t){if(!e.uuid)return null;let n=wf(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 gf(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await _t(e,t)}catch{return[]}let o=[],s=new Map,a=new Map;for(let i of r.split(`
|
|
101
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let b=l.attachment;b?.type==="queued_command"&&b.commandMode==="task-notification"&&typeof b.prompt=="string"&&b.prompt.trim()&&o.push({role:"user",text:b.prompt.trim(),ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let u=l.message;if(!u)continue;let d=u.role||l.type;if(d!=="user"&&d!=="assistant"||d==="assistant"&&u.model==="<synthetic>"&&!l.isApiErrorMessage)continue;if(d==="assistant"&&Array.isArray(u.content))for(let b of u.content){let x=b?.type==="tool_use"?b.input?.file_path||b.input?.path:null;typeof x=="string"&&mN.test(x)&&s.set(b.id,x.split("/").pop()),b?.type==="tool_use"&&b.name==="Skill"&&b.id&&a.set(b.id,String(b.input?.skill||"").trim().replace(/^\//,"")||null)}if(fN(u.content)){let b=Dk(l,s);b&&o.push({role:"assistant",text:"",images:b,ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let p=dN(u.content),f=d==="assistant"?pN(u.content):null,w=d==="user"?Dk(l,s):null;if(!p&&!f&&!w)continue;let m=d==="user"&&l.isMeta&&typeof l.sourceToolUseID=="string"&&l.sourceToolUseID?{toolUseId:l.sourceToolUseID,skill:a.get(l.sourceToolUseID)??null}:null,g=d==="assistant"&&l.isApiErrorMessage?{code:typeof l.error=="string"?l.error:null,status:Number(l.apiErrorStatus)||null,requestId:l.requestId||null,cls:pi({error:l.error,status:l.apiErrorStatus,text:p})}:null;o.push({role:d,text:p,tools:f||void 0,images:w||void 0,injected:m||void 0,ts:l.timestamp?Date.parse(l.timestamp):null,model:u.model||null,apiError:g||void 0})}return o.slice(-n)}async function jk(e,t,{turns:n=16}={}){let r=uN(e,t);if(!r)return"";let o=await gf(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}:** ${hN(s.text||c,700)} ${i}`.trim()}).join(`
|
|
102
|
+
|
|
103
|
+
`):""}var mN,hN,Ll=q(()=>{Bl();nn();mN=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;hN=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import wN from"node:fs/promises";import{execFile as gN}from"node:child_process";import{promisify as yN}from"node:util";function Uk(e){bf=typeof e=="function"?e:null}function kf(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Hk({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Bk.has(r)||(Bk.add(r),Lk=Lk.then(()=>kN({sessionName:e,from:t,to:r})).catch(()=>{}))}async function kN({sessionName:e,from:t,to:n}){await le({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await yf("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await wN.realpath(r),s=kf(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await yf("npm",["install","-g","@openai/codex"],{env:a,timeout:bN});let i=(await yf(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await le({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await Fk({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await le({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await Fk({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function Fk(e){if(bf)try{await bf(e)}catch{}}var yf,bN,Bk,Lk,bf,Fl=q(()=>{kn();yf=yN(gN),bN=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Bk=new Set,Lk=Promise.resolve(),bf=null});function vN(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
104
|
+
`);if(!Wk.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(xN);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"&&Wk.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 So(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
105
|
+
`);for(let r=n.length-1;r>=0;r--)if(Gk.test(n[r]))return n.slice(r).join(`
|
|
106
|
+
`);return t}function SN(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 TN(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 AN(e,t=""){let n=So(e);if(fi.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
107
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(qk);if(!u)continue;s<0&&(s=l);let d="";for(let p=l+1;p<Math.min(l+3,r.length);p++){let f=r[p].trim();if(f){if(qk.test(r[p])||/^Press enter/.test(f))break;d=f;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&&!Gk.test(r[l])&&SN(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=TN(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function IN(e,t=""){let n=So(e);if(fi.test(n)||!EN.test(n))return null;let r=n.trimEnd().split(`
|
|
108
|
+
`).pop()||"";return _N.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 RN(e,t=""){let n=So(e);if(fi.test(n)||!Kk.test(n))return null;let r=n.split(`
|
|
109
|
+
`),o=r.findIndex(i=>Kk.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match($N);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 ON(e,t=""){let n=So(e);return fi.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 CN(e,t=""){let n=So(e);return fi.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 MN(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||xf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
110
|
+
`).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 PN(e,t=""){let n=String(e);if(!NN.test(n)||xf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
111
|
+
`),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 DN(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||xf.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 zk(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=IN(t,n)||RN(t,n)||ON(t,n)||CN(t,n)||AN(t,n);if(r)return r}if(e==="cursor"){let r=PN(t,n)||DN(t,n)||MN(t,n);if(r)return r}return vN(t,{provider:e,box:n})}function Vk(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 xN,Wk,fi,Gk,qk,EN,_N,Kk,$N,xf,NN,Ul=q(()=>{xN=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Wk=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;fi=/(^|\n)\s*›/,Gk=/^\s*Welcome to Codex\b/;qk=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;EN=/Successfully logged in\./,_N=/^\s*Press Enter to continue\.\s*$/;Kk=/Enter this one-time code/,$N=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;xf=/Run Everything|Add a follow-up|Plan, search, build anything/;NN=/click this link to log in|Signing in with the browser/});function FN(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
112
|
+
`),n=-1;for(let c=t.length-1;c>=0;c--)if(t[c].trim()){n=c;break}if(n<0)return!1;let r=t[n];if(BN.test(r)||LN.test(r)){for(let c=n-1;c>=0&&n-c<=64;c--)if(/^\s*›/.test(t[c]))return!0}let s=String(e||""),a=s.split(`
|
|
113
|
+
`).filter(c=>/^\s*›/.test(c)),i=a.some(c=>/^\s*›\s+(?:Ask Codex to do anything|Implement \{feature\})\s*$/.test(c));return a.length>0&&!i&&/esc to interrupt|\bWorking \(/.test(s)}function KN(e){let t=String(e||"");if(!Je.codex.block.test(t))return null;let n=So(t);if(!Je.codex.block.test(n))return null;let r=n.match(qN);return{from:r?.[1]||null,to:r?.[2]||null}}function xn(e){return new Promise(t=>setTimeout(t,e))}function vf(e,t){let n=Date.now(),r=GN();for(let[s,a]of Wl)n-a>=r&&Wl.delete(s);let o=`${e}::${t}`;return Wl.has(o)?!1:(Wl.set(o,n),!0)}function Zk(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
114
|
+
`),r=-1;for(let o=n.length-1;o>=0;o--)if(Xk.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
115
|
+
`)}function VN(e){let t=0;for(let n of String(e).split(`
|
|
116
|
+
`))Xk.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Sf(e,t){let n=Je[e];if(!n?.busy)return{state:"idle",reason:null};if(Af(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(Zk(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=VN(t);return e==="claude"&&r>0&&r<zN?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Tf(e,t){return Sf(e,t).state}function Qk(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
117
|
+
`).slice(-YN).join(`
|
|
118
|
+
`);return JN.test(n)}function Cs(e,t){return Tf(e,t)==="busy"}function ql(e,t){return Tf(e,t)!=="idle"}function Ms(e,t){let n=Je[e];return n?.up?n.up.test(t):!0}function XN(e,t){let n=Je[e];return n?Af(e,t)?!1:n.up.test(t)&&Tf(e,t)==="idle":!0}function Af(e,t){let n=Je[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 Ef(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
117
119
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
118
|
-
`);return e}function
|
|
119
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
120
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
121
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
122
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
123
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
124
|
-
`):""}async function
|
|
120
|
+
`);return e}function e0(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function Jk(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 Yk(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await bo(e).catch(()=>{});;){let l=Ef(await je(e),o);if(XN(t,l))return await s?.(),await xn(Os),"up";if(Je[t]?.up&&!Je[t].up.test(l))if(await Kn(e)){if(c??=Date.now(),Date.now()-c>=ai()){let u=Jk(t);throw le({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"&&WN.test(l)&&!Je.claude.up.test(l))return le({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{}),"dialog-bail";if(t==="codex"&&HN.test(l)&&!Je.codex.up.test(l)&&i<3){if(!vf(e,"codex-trust")){await xn(Hl);continue}i+=1,await le({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await nt(e,"",{enter:!0,kind:"codex-trust-accept"}),await xn(Os);continue}if(t==="cursor"&&UN.test(l)&&!Je.cursor.up.test(l)&&i<3){if(!vf(e,"cursor-trust")){await xn(Hl);continue}i+=1,await le({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await nt(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await xn(Os);continue}if(t==="codex"&&i<3){let u=KN(l);if(u){if(!vf(e,`codex-update:${u.from}->${u.to}`)){await xn(Hl);continue}i+=1,await le({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await nt(e,"3",{enter:!1,kind:"codex-update-skip"}),Hk({sessionName:e,from:u.from,to:u.to}),await xn(Os);continue}}if(Date.now()>a){if(Af(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=Je.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"&&Je.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"&&Je.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"&&Je.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'),le({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await Kn(e)){let u=Jk(t);throw le({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u.message}).catch(()=>{}),u}return console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),le({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!Je[t]?.block?.test(l),paneUp:!!Je[t]?.up?.test(l),paneBusy:!!Je[t]?.busy?.test(Zk(Je[t],l))}).catch(()=>{}),await xn(Os),"timeout"}await xn(Hl)}}function _f({agent:e,agentCmd:t}={}){let n=t?Ar(t):Ar(e||"");return{label:n,realClaude:t?yo(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 t0({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=_f({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function mi({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}={}){let{realCodex:s,realCursor:a}=_f({agent:n,agentCmd:r});return!t||!s&&!a?null:$r({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).then(()=>{},()=>{})}async function $r({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=jN(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:p}=_f({agent:r,agentCmd:o});if(u&&e&&await yr(e),d&&e&&await ps(e),p&&e&&await fs(e),(u||d||p)&&n)return await Yk(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});let f=Je[r]?r:Je[l]?l:null;return process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f?await Yk(n,f,s,{launchEcho:i}):(await xn(Os),"settle")}var jN,Hl,Os,BN,LN,Je,UN,HN,WN,qN,GN,Wl,zN,Xk,JN,YN,hi=q(()=>{ct();Ua();cl();ll();Fl();Ul();kn();jN=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Hl=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Os=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,BN=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,LN=/^\s*tab to queue message\b/;Je={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:FN},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/}},UN=/Trust this workspace|\[a\]\s*Trust/,HN=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,WN=/Resume from summary/i,qN=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;GN=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Wl=new Map;zN=68,Xk=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;JN=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,YN=15});function tP(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
121
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=eP}function vn(e,t){return e==="codex"?{enter:!0,submitDelayMs:ZN,...tP(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:QN}:{enter:!0}}var ZN,QN,eP,wi=q(()=>{ZN=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),QN=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),eP=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function gi(e,t){if(e==="codex")return aP(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
122
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(n0.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(n0.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(nP);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 aP(e){let t=String(e).replace(/\r/g,"").split(`
|
|
123
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(rP.test(t[o])||oP.test(t[o]))&&!If.test(t[o])?n=o:If.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(If);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(sP.test(s))return null;r.unshift(s)}return null}function $f(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=He(n.replace(iP,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!He(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function He(e){return String(e||"").replace(/\s+/g," ").trim()}function r0(e,t,{slackMs:n=2e4}={}){let r=He(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&&He(a.text).includes(r));return s>=0&&s<o.length-1}function o0(e,t,n,{busy:r=!1}={}){let o=gi(e,n);return o&&Kl(t,o)||o&&e==="codex"&&$f(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Kl(e,t){let n=He(e),r=He(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var n0,nP,If,rP,oP,sP,iP,yi=q(()=>{n0=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,nP=/^❯[ ]?(.*)$/,If=/^\s*›[ ]?(.*)$/,rP=/\S+ · \S/,oP=/^\s*(?:\? for shortcuts|tab to queue message)\b/,sP=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;iP=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function ki({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say",stats:a=null},i={}){let c=(i.now||Date.now)(),l=(H,P,E={})=>(a&&typeof a=="object"&&Object.assign(a,{verdict:H,retries:E.retries??0,ms:(i.now||Date.now)()-c,sawUp:!!E.sawUp,reason:E.reason??null}),P);if(!cP.has(t))return l("skipped",!0);let u=i.waitForUp??t0({agent:t,agentCmd:n}),d=i.capture||(H=>je(H,{lines:tt})),p=i.pressEnter||(H=>Ke(H,["Enter"],{kind:"submit-retry"})),f=i.sleep||pP,w=i.now||Date.now,m=i.log||(H=>le(H).catch(()=>{})),g=i.maxMs??dP(),b=w()+g,x={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},y=0,$=0,O=0,M=!1;for(await f(bi[0]);;){let H=await d(e).catch(()=>""),P=Ms(t,H),E=Cs(t,H),R=o0(t,r,H,{busy:E});if(R==="submitted")return l("submitted",!0,{retries:y,sawUp:!0});if(u&&(!P||R==="stuck"&&E)){if(w()>=b)return R==="stuck"?(m({...x,phase:"submit-failed",reason:"stuck-at-deadline",retries:y}),l("failed",!1,{retries:y,sawUp:M,reason:"stuck-at-deadline"})):(m({...x,phase:"submit-unverified",reason:M?"cli-down":"cli-never-up",retries:y,waitedMs:g}),l("unverified",!0,{retries:y,sawUp:M,reason:M?"cli-down":"cli-never-up"}));await f(lP);continue}if(M=!0,R==="stuck"){if(y>=uP)return m({...x,phase:"submit-failed",retries:y}),l("failed",!1,{retries:y,sawUp:!0,reason:"stuck"});y+=1,$=0,O=0,await p(e),m({...x,phase:"submit-retry",attempt:y}),await f(bi[0]);continue}if($+=1,$>=bi.length||w()>=b)return l("assumed",!0,{retries:y,sawUp:!0});O+=1,await f(bi[Math.min(O,bi.length-1)])}}var cP,bi,lP,uP,dP,pP,Rf=q(()=>{ct();hi();yi();kn();cP=new Set(["claude","codex"]),bi=[250,450,700],lP=500,uP=2,dP=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},pP=e=>new Promise(t=>setTimeout(t,e))});function s0(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function zn(e){let t=s0(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of fP){let o=0;for(let s of mP[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function a0(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))hP.has(r)&&(t+=o||0),wP.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function xi(e){return String(e||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}function i0(e){let t=s0(e).trim();return t?yP.test(t)?!0:t.split(/\s+/).length<=gP:!1}var fP,mP,hP,wP,gP,yP,Gl=q(()=>{fP=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],mP={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope (it |this |that |them )?out\b/,/\btalk (me |us )?through\b/,/\b(give|show|tell) me\b/,/\bwhat about\b/,/\bwhy\b/,/\btake a look\b/,/\bwalk (me |us )?through\b/,/\bexplain\b/,/\bdo we (still |even )?need\b/,/\bis there (a|an|any)\b/,/\bhow often\b/,/\bdo we (currently|already)\b/,/\bwould it be (valuable|useful|worth)\b/,/\bis it possible\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/,/\bkeeps? getting\b/,/\bdoesn'?t (seem|look) right\b/,/\bseems? (wrong|off|broken)\b/,/\b(too slow|slower than|much slower)\b/,/\bfail(ed|ing|s)\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/,/\bupdate (the |this |that |my |our )?[\w-]+( [\w-]+){0,3} to\b/,/\bset up\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/,/\b(linked[ -]?in|social( media)?) post\b/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/,/\b(unzip|decompress|untar)\b/]};hP=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),wP=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);gP=12,yP=/^\s*(ok(ay)?|yes|yep|yeah|sure|go ahead|go|proceed|continue|next|begin|start|do it|do that|try again|try once more|again|ship it|perfect|great|good|nice|thanks|lock(ed)? (it |that |those )?in|option [a-z]\b|where are we|what'?s the status)\b/i});function d0(e){let t=xi(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||bP.has(r)||n.push(r);return n}function kP(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(u0(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(u0(n,"assistant",o))}}return t}function u0(e,t,n){let r=zn(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 vi(e){let t=kP(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=d0(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 p=n.get(u);p||n.set(u,p=new Map),p.set(i,d)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function zl(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(d0(t))];if(!i.length)return[];let c=new Map;for(let u of i){let d=e.postings.get(u);if(!d)continue;let p=d.size,f=Math.log(1+(e.N-p+.5)/(p+.5));for(let[w,m]of d){let g=e.lengths[w],b=m+c0*(1-l0+l0*(g/(e.avgdl||1))),x=f*(m*(c0+1)/b);c.set(w,(c.get(w)||0)+x)}}let l=[];for(let[u,d]of c){let p=e.chunks[u];o&&p.role!==o||s&&!p.tags.includes(s)||a&&p.boxId!==a||l.push({chunk:p,score:d})}return l.sort((u,d)=>d.score-u.score||d.chunk.seq-u.chunk.seq),l.slice(0,r)}var c0,l0,bP,Of=q(()=>{Gl();c0=1.5,l0=.75,bP=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 Vl from"node:fs/promises";import p0 from"node:path";async function xP(e){let t=p0.join(e,".bostrat","events"),n;try{n=await Vl.readdir(t)}catch{return vi([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Vl.readFile(p0.join(t,o),"utf8");for(let a of s.split(`
|
|
124
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return vi(r)}function vP(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}
|
|
125
|
+
${o}`.trim()||t.slice(0,2e3)}function SP(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=zl(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,p=`- **${l}:** ${d}`;if(i+p.length>r||(a.push(p),i+=p.length,a.length>=n))break}return a.length?["",`## ${f0}, 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(`
|
|
126
|
+
`):""}async function m0(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Vl.readFile(t,"utf8");if(a.includes(f0))return{appended:!1,reason:"already-enriched"};let i=await xP(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=SP(i,vP(a),{k:r,maxChars:o,exclude:s});return c?(await Vl.appendFile(t,`
|
|
125
127
|
`+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
|
|
128
|
+
- `).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 f0,h0=q(()=>{Of();f0="Relevant prior discussion (verbatim, retrieved"});function $P(e){return TP.test(String(e||"").trimStart())}function y0(e){let t=String(e||"");if(!$P(t.trimStart()))return null;let n=t.replace(AP,"").trim(),r=RP(n),o=[];for(let u of _P){let d=u.from.map(p=>Ns(r.get(p))).find(p=>p&&!w0(p));d&&o.push({h:u.h,text:Si(d,EP)})}let s=Ns(r.get("Current Work"))||Ns(r.get("Primary Request and Intent"))||CP(n),a=Yl(s),i=Si(Jl(Nf(a[0]||"")),IP)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Si(Jl(Nf(l)),Mf));for(let u of["Pending Tasks","Optional Next Step"]){let d=Yl(Ns(r.get(u)))[0]||"";if(!(!d||w0(d))){c.push(Si(`Left to do: ${Jl(Nf(d))}`,Mf));break}}if(!c.length){let u=Yl(Ns(r.get("Problem Solving"))||"")[0];u&&c.push(Si(Jl(u),Mf))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function RP(e){let t=new Map,n=[];Cf.lastIndex=0;for(let r=Cf.exec(e);r;r=Cf.exec(e))n.push({name:OP(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 OP(e){return g0.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Ns(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
129
|
+
`).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 CP(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Ns(t.split(/\n{2,}/).slice(0,2).join(" "))}function Yl(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 Nf(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 Jl(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function w0(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function Si(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Yl(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 TP,g0,Cf,AP,EP,_P,IP,Mf,b0=q(()=>{TP=/^This session is being continued from a previous conversation that ran out of context\./,g0=["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"],Cf=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${g0.join("|")})[:*\t ]*$`,"gim"),AP=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,EP=420,_P=[{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"]}],IP=116,Mf=168});function x0(e){return Pf.has(String(e||"").replace(/^\//,"").toLowerCase())}function Ti(e){let t=String(e||"").trim();if(!t)return null;let n=y0(t);if(n)return n;let r=t.match(MP);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(NP);if(o)return{kind:"skill",name:o[1]};let s=t.match(PP);if(s)return{kind:"skill",name:s[1].toLowerCase()};let a=t.match(DP);if(a){let c=a[1].toLowerCase();return Pf.has(c)?{kind:"suppress"}:{kind:"command",command:`/${c}`}}let i=t.match(GP);if(i&&Pf.has(i[1].toLowerCase()))return{kind:"suppress"};if(jP.test(t))return{kind:"suppress"};if(LP.test(t))return{kind:"suppress"};if(BP.test(t))return{kind:"suppress"};if(FP.test(t)){let c=u=>zP(t.match(u)?.[1].trim()||"")||null,l=c(KP);return{kind:"task",summary:c(UP)||"",taskId:c(HP),toolUseId:c(WP),status:c(qP)?.toLowerCase()||null,resultHead:l?l.length>k0?`${l.slice(0,k0).trimEnd()}\u2026`:l:null}}return null}function v0(e){let t=Ti(e?.text);return t||(e?.injected?{kind:"skill",name:e.injected.skill||"skill"}:null)}function zP(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function S0(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 T0(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var MP,NP,PP,DP,jP,BP,LP,FP,UP,HP,WP,qP,KP,k0,Pf,GP,Xl=q(()=>{b0();MP=/^Base directory for this skill:\s*(\S+)/,NP=/^Run the "([^"]+)" workflow\./,PP=/^#\s*\/([a-z0-9:_-]+)\s*[—–:-]/i,DP=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,jP=/^<local-command-(?:stdout|caveat)>/i,BP=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,LP=/^Continue from where you left off\.$/,FP=/^<task-notification>/i,UP=/<summary>([\s\S]*?)<\/summary>/i,HP=/<task-id>([\s\S]*?)<\/task-id>/i,WP=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,qP=/<status>([\s\S]*?)<\/status>/i,KP=/<result>([\s\S]*?)(?:<\/result>|$)/i,k0=600,Pf=new Set(["model","clear","compact","login","exit","quit"]);GP=/^\/([a-z0-9:_-]+)$/i});import VP from"node:crypto";function ZP(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,E0)){let o=Ai(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,JP)){let c=Ai(i?.label);if(!c)return null;let l=Ai(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<YP)return null;n.push({question:o,header:Ai(r?.header).slice(0,XP),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Df(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;A0.lastIndex=0;let t=null;for(let o;o=A0.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=ZP(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function jf(e,t,n){let r=VP.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Bf(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=E0}={}){if(!Array.isArray(e)||!t||!n)return null;let s=null;for(let l=e.length-1;l>=0;l--){let u=e[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==t||s.agent!==n)return null;let a=s.ask?.questions||Df(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=jf(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:t,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function _0(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=Ai(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
128
130
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
129
131
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
130
|
-
${o}`),s||""}var
|
|
132
|
+
${o}`),s||""}var E0,JP,YP,XP,A0,Ai,I0,Lf=q(()=>{E0=4,JP=4,YP=2,XP=24,A0=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Ai=e=>typeof e=="string"?e.trim():"";I0=`
|
|
131
133
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
132
134
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
133
135
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -147,76 +149,76 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
147
149
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
148
150
|
anything you can decide yourself; keep asking in prose for questions without
|
|
149
151
|
enumerable options.
|
|
150
|
-
`});function lP(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 dP(e,t,n){let r=l0(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 xn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,d=(b,m)=>{u.set(b,m),t.push(m)},p=b=>{let m=u.get(b);m&&(m.retired=!0)},f=/^Agent "([\s\S]+)" finished$/,h=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let m=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let v of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let D=v[0].split("/").pop().toLowerCase();h.has(D)||h.set(D,[]),h.get(D).push(b.payload?.ts??b.ts)}}let w=3*6e4,g=20*6e4,k=(b,m)=>!!(b?.name&&h.get(String(b.name).toLowerCase()))?.some(D=>D>=m-w&&D<=m+g);for(let b of e||[])if(b.type==="turn.retracted"){r.add(`${b.boxId}|${b.payload?.saidTs}`);let m=ze(b.payload?.text);m&&(o.has(b.boxId)||o.set(b.boxId,[]),o.get(b.boxId).push(m))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let m=ze(b.payload?.text);m&&(s.has(b.boxId)||s.set(b.boxId,new Set),s.get(b.boxId).add(m))}else if(b.type==="job.started"&&b.payload?.id)a.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&c.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let m=b.payload.note,v={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:he(b)};m.toolUseId&&i.set(m.toolUseId,v);let D=String(m.summary||"").match(f);D&&l.set(D[1],v)}let x=new Map,y=new Map,I=null,R=new Map,P=new Map,H=new Map,j=new Map,A=new Map,O=new Map,N=new Map,z=new Map,$=new Map,T=new Map,M=new Map,W=new Map,K=new Map,be=new Map,se=new Map,we=[],ae=b=>we.push(b),V=b=>{if(!we.length)return;let m=he(b);for(let v of we){if(v.status!=="open"||v.box&&v.box!==b.boxId||(v.ts??0)>m)continue;let D=t.indexOf(v);D>=0&&t.splice(D,1),v.ts=m,t.push(v)}},de=new Map,re=new Map,Y=new Map,$e=new Map,Pe=new Map,me=new Map,_=new Map,F=new Map,ge=()=>{for(let b of F.values())b.questions.length&&t.push(b);F.clear()},Ne=null,Be=6e4,St=new Map,hn=(b,m)=>{let v=o.get(b);if(!v?.length)return!1;let D=ze(m),le=s.get(b);return v.some(ye=>{if(!D.startsWith(ye))return!1;let Ue=ze(D.slice(ye.length));return!!Ue&&!!le?.has(Ue)})};for(let b of e||[]){let m=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"||(we.length=0,!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length))break;{let v=m.note||Rs(m.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let D=String(v.summary||"").match(f);if(!v.toolUseId&&D&&c.has(D[1]))break}if(v.kind==="command"&&n0(v.command))break;if(v.kind==="resume-summary"){t.push({id:b.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:he(b),box:b.boxId,agent:b.provider});break}v.kind!=="suppress"&&t.push({id:b.id,kind:"event",type:o0(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:he(b),box:b.boxId,agent:b.provider});break}}if(r.has(`${b.boxId}|${he(b)}`)||b.source==="transcript"&&hn(b.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){t.push({id:b.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:he(b),box:b.boxId,agent:b.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"){m.kind==="prompt-answer"&&(Ne=null);break}for(let v of R.values())v.status="stale";R.clear();for(let v of H.values())v.status="stale";H.clear();for(let v of K.values())v.status="stale";K.clear(),Ne&&Ne.box===b.boxId&&(t.push(lP(Ne)),Ne=null),t.push({id:b.id,kind:"message",role:"user",text:m.text,ts:he(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...b.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let v=m.from||{};t.push({id:b.id,kind:"event",type:"peer-message",text:m.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:he(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let v=wk(m,{codexFailedText:ho});if(v){t.push({id:b.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:b.provider,legacy:!0,at:he(b),ts:he(b),box:b.boxId,agent:b.provider});break}ge();let D=(m.images||[]).filter(Ce=>!k(Ce,he(b)));if(!String(m.text||"").trim()&&!D.length)break;if(b.provider==="cursor"){let Ce=`${b.boxId}|${m.text}`;if(n.has(Ce))break;n.add(Ce)}(!Ne||Ne.box===b.boxId)&&(Ne=null);let le=b.provider==="codex"?(m.tools||[]).filter(Ce=>!of.has(Ce)&&!ai.has(Ce)):m.tools||[],ye=m.text,Ue=null,gt=Ef(String(m.text||""));gt&&(ye=gt.text,Ue={questions:gt.questions}),t.push({id:b.id,kind:"message",role:"assistant",text:ye,ts:he(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...Ue?{ask:Ue}:{},...le.length?{tools:le}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...D.length?{images:D}:{}}),V(b),Ue&&(Ne={id:b.id,groupId:_f(b.provider,m.sourceId||b.id,Ue.questions),provider:b.provider,box:b.boxId,questions:Ue.questions,ts:he(b)});break}case"job.started":{if(!m.id)break;let v=he(b),D=i.get(m.id);if(!D&&(m.kind||"agent")==="agent"&&m.name){let Ce=l.get(m.name);Ce&&Ce.ts>=v&&(D=Ce)}let le={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":"running",startedTs:v,finishedTs:D?.ts??null,summary:D?.summary??null,resultHead:D?.resultHead??null},ye=St.get(b.boxId),Ue=ye?.jobs[ye.jobs.length-1]?.startedTs;if(ye&&Ue!=null&&v-Ue<=Be)ye.jobs.push(le);else{let Ce={id:b.id,kind:"job-dock",dockId:m.id,ts:v,box:b.boxId,agent:b.provider,jobs:[le]};St.set(b.boxId,Ce),t.push(Ce)}break}case"handoff.done":t.push(it(b,"lead-handoff"));break;case"subtask.delegated":t.push({...it(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":t.push(it(b,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let D of R.values())D.status="stale";R.clear();for(let D of H.values())D.status="stale";H.clear()}let v=it(b,`pr-${m.change}`);if(v.box=b.boxId??null,uP.has(m.change)&&m.number!=null){let D=dP(x,y,m);if(x.size>=2){for(let ye of y.values())for(let Ue of ye)Ue.grouped=!0;v.groupPrs=[...x.values()];let le=[v];for(let ye of x.keys())y.set(ye,le)}else{let le=y.get(D)??[];le.push(v),y.set(D,le)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let D=l0(m),le=I&&!I.complete||x.size>=2;if(x.has(D))x.delete(D),y.delete(D);else if(m.number!=null)for(let ye of[...x.keys()])ye.endsWith(`#${m.number}`)&&(x.delete(ye),y.delete(ye));if(le&&m.number!=null){(!I||I.complete)&&(I={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:he(b),ts:he(b),box:b.boxId,agent:b.provider},t.push(I)),I.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:he(b)}),I.remaining=[...x.values()],I.complete=x.size===0;break}}t.push(v);break}case"pr.offered":{if(!m.offerId)break;for(let D of R.values())D.status="stale";R.clear();for(let D of H.values())D.status="stale";H.clear();let v={id:b.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:b.source==="cli"||m.via==="cli"?"cli":"transcript",ts:he(b),box:b.boxId,agent:b.provider};R.set(m.offerId,v),P.set(m.offerId,v),t.push(v),v.source==="cli"&&ae(v);break}case"pr.offer.answered":{let v=P.get(m.offerId);v&&(v.status=m.action||"dismiss",v.actions=Array.isArray(m.actions)?m.actions:null,v.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(v.existingPr={number:m.existingPr.number??null}),R.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let D of H.values())D.status="stale";H.clear();let v={id:b.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:he(b),box:b.boxId,agent:b.provider};H.set(m.offerId,v),j.set(m.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=j.get(m.offerId);v&&(v.status=m.choice||"dismiss",H.delete(m.offerId));break}case"box.state.changed":{let v=m.change==="model"?"model-change":m.change;if(!v)break;let D=it(b,v);if(v==="model-change"&&(D.to=m.model),v==="node-update"&&D.latest){let le=t.findIndex(ye=>ye.kind==="event"&&ye.type==="node-update"&&ye.latest===D.latest);if(le>=0){let ye=t[le];t[le]={...D,ts:ye.ts,at:ye.at,firstAt:ye.firstAt??ye.ts,updatedAt:D.ts};break}}t.push(D);break}case"track.transferred":case"track.recovered":t.push(it(b,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){p(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;d(`view:${m.viewId}`,{id:b.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:he(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{t.push({id:b.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:he(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(m.change==="picked"){t.push({id:b.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:he(b),box:b.boxId,agent:b.provider});break}if(m.change==="deleted"){p(`mock:${m.mockId}`);break}if(m.change!=="created")break;d(`mock:${m.mockId}`,{id:b.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:he(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(m.change==="deleted"){p(`flow:${m.flowId}`);break}if(m.change!=="created")break;d(`flow:${m.flowId}`,{id:b.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:he(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){p(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;d(`runbook:${m.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:he(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{t.push({id:b.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:he(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let D of N.values())D.box===(m.box||b.boxId)&&(D.status="stale",N.delete(D.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:he(b),box:m.box||b.boxId,agent:m.agent||b.provider};N.set(m.offerId,v),z.set(m.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=z.get(m.offerId);v&&(v.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",N.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let D of A.values())D.key===m.key&&(D.status="stale",A.delete(D.offerId));let v={id:b.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:he(b),box:b.boxId,agent:b.provider};A.set(m.offerId,v),O.set(m.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=O.get(m.offerId);v&&(v.status=m.action==="moved"?"moved":"dismiss",A.delete(m.offerId));break}case"ticket.state.changed":{for(let v of A.values())v.key===m.key&&v.to===m.to&&(v.status="stale",A.delete(v.offerId));if(m.mode==="offer")break;t.push(it(b,"ticket-state"));break}case"ticket.commented":{t.push({id:b.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:he(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":t.push(it(b,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let D of $.values())D.status="stale";$.clear();let v={id:b.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:he(b),box:b.boxId,agent:b.provider};$.set(m.proposalId,v),T.set(m.proposalId,v),t.push(v),ae(v);break}case"ticket.create.answered":{let v=T.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",v.createdCount=m.createdCount??null,v.failedCount=m.failedCount??null,$.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let D of M.values())D.status="stale";M.clear();let v={id:b.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:he(b),box:b.boxId,agent:b.provider};M.set(m.proposalId,v),W.set(m.proposalId,v),t.push(v),ae(v);break}case"loop.create.answered":{let v=W.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",M.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let v=m.replaces?se.get(m.replaces):null;v&&v.status==="armed"&&(v.status="replaced");let D={id:b.id,kind:"followup-armed",armId:m.armId,trigger:m.trigger??"now",label:m.label??"",prompt:m.prompt??"",why:m.why??null,status:"armed",ts:he(b),box:b.boxId,agent:b.provider};se.set(m.armId,D),D.trigger!=="now"&&t.push(D);break}case"followup.armed.cancelled":{let v=se.get(m.armId);v&&v.status==="armed"&&(v.status="cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let le of K.values())le.trigger===m.trigger&&(le.status="stale",K.delete(le.offerId));let v=Array.isArray(m.rows)?m.rows:[];for(let le of v){let ye=le.armId?se.get(le.armId):null;ye&&ye.status==="armed"&&(ye.status="offered")}let D={id:b.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},rows:v.map(le=>({followupId:le.followupId??null,armId:le.armId??null,label:le.label??"",prompt:le.prompt??null,promptPreview:le.promptPreview??null,promptHash:le.promptHash??null,origin:le.origin??"operator",why:le.why??null})),status:"open",answer:null,ts:he(b),box:b.boxId,agent:b.provider};K.set(m.offerId,D),be.set(m.offerId,D),t.push(D),D.trigger==="now"&&ae(D);break}case"followup.answered":{let v=be.get(m.offerId);v&&(v.status=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss",v.answer={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0},K.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let D of de.values())D.status="stale";de.clear();let v={id:b.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:he(b),box:b.boxId,agent:b.provider};de.set(m.proposalId,v),re.set(m.proposalId,v),t.push(v),ae(v);break}case"followup.create.answered":{let v=re.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",de.delete(m.proposalId));break}case"followup.created":{let v=m.proposalId?re.get(m.proposalId):null;v&&(v.created={followupId:m.followupId??null});break}case"loop.created":{let v=m.proposalId?W.get(m.proposalId):null;v&&(v.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(it(b,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let le of Y.values())le.loopId===m.loopId&&(le.status="stale",le.staleReason="superseded",Y.delete(le.proposalId));let v=b.type==="loop.update.offered",D={id:b.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...v?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:he(b),box:b.boxId,agent:b.provider};Y.set(m.proposalId,D),$e.set(m.proposalId,D),t.push(D),ae(D);break}case"loop.update.answered":{let v=$e.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",Y.delete(m.proposalId));break}case"loop.delete.answered":{let v=$e.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",Y.delete(m.proposalId));break}case"loop.updated":{let v=m.proposalId?$e.get(m.proposalId):null;v&&(v.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(it(b,"loop-updated"));break}case"loop.deleted":t.push(it(b,"loop-deleted"));break;case"loop.paused":t.push(it(b,"loop-paused"));break;case"planning.change.offered":{if(!m.proposalId||!Array.isArray(m.ops)||!m.ops.length)break;for(let D of Pe.values())D.status="stale",D.staleReason="superseded",Pe.delete(D.proposalId);let v={id:b.id,kind:"planning-offer",proposalId:m.proposalId,why:m.why??"",ops:m.ops,base:m.base??{},resolved:m.resolved??{},snapped:!!m.snapped,createsMeRow:!!m.createsMeRow,status:"open",staleReason:null,applied:null,ts:he(b),box:b.boxId,agent:b.provider};Pe.set(m.proposalId,v),me.set(m.proposalId,v),t.push(v),ae(v);break}case"planning.change.answered":{let v=me.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",Pe.delete(m.proposalId));break}case"planning.changed":{let v=m.proposalId?me.get(m.proposalId):null;v&&(v.applied={results:Array.isArray(m.results)?m.results:[]}),t.push(it(b,"planning-changed"));break}case"rework.cause.offered":{if(!m.sha||_.has(m.sha))break;let v={id:b.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,intent:m.intent??null,shas:Array.isArray(m.shas)?m.shas:null,pr:m.pr??null,retracts:0,status:"open",cause:null,ts:he(b),box:b.boxId,agent:b.provider};_.set(m.sha,v),t.push(v);break}case"rework.cause.answered":{let v=m.sha?_.get(m.sha):null;v&&(v.status="answered",v.cause=m.cause??null);break}case"rework.cause.retracted":{let v=m.sha?_.get(m.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},D=m.proposalId?T.get(m.proposalId):null;if(D){D.created=v;break}t.push({id:b.id,kind:"ticket-created",...v,ts:he(b),box:b.boxId,agent:b.provider});break}case"error":{if(!m.kind)break;t.push(it(b,m.kind));break}case"provider.error":t.push({...it(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":t.push(it(b,"usage-cap-hit"));break;case"usage.cap.warning":t.push(it(b,"usage-cap-warning"));break;case"provider.connected":{t.push({id:b.id,kind:"event",type:"provider-connected",provider:m.provider||b.provider,ts:he(b),at:he(b),box:b.boxId,agent:b.provider});break}case"prompt.answered":{if(Ne=null,m.control||!m.question)break;let v=m.groupId||m.promptId;if(!v)break;let D=F.get(v);D||(D={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:he(b)},F.set(v,D));let le=m.questionIndex??0,ye={index:le,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},Ue=D.questions.findIndex(Ce=>Ce.index===le&&ze(Ce.question)===ze(ye.question));Ue>=0?D.questions[Ue]=ye:D.questions.push(ye),D.questions.sort((Ce,Ra)=>Ce.index-Ra.index),D.questionTotal=Math.max(D.questionTotal,m.questionTotal??1),D.questions.length>=D.questionTotal&&(t.push(D),F.delete(v));break}default:break}}let wt=u.size?t.filter(b=>!b.retired):t;return fP(wt.sort((b,m)=>(b.ts??0)-(m.ts??0)))}function fP(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<=pP){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 Rf(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:mP(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??xn(e)).filter(o=>o.kind==="message");return ck(n,r)}function mP(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Ts(e.command)?t:Sr(e.command,e.description||"")}function it(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:he(e),ts:he(e)}}function u0(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)+hP,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&wP.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function d0(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!yP.has(r.type)||!r.box)continue;let o=(r.ts??0)+gP,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 Wl(e){return(e||[]).filter(t=>bP.has(t?.kind)&&t.status==="open")}var he,uP,l0,pP,hP,wP,gP,yP,bP,ql=q(()=>{As();wo();Il();Dl();Hl();$f();he=e=>e.payload?.ts??e.ts;uP=new Set(["opened","ci-fixed","reconciled","pushed"]),l0=e=>`${e.repo??""}#${e.number}`;pP=2*6e4;hP=12e4,wP=new Set(["archived","unarchived","opened"]);gP=2500,yP=new Set(["pr-opened","pr-merged","pr-auto-armed"]);bP=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","planning-offer","fullscreen-offer","followup-create-offer"])});function TP(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 EP(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 _P(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<vP&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<SP&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function h0(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=TP(e),s=EP(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=xn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-kP);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(m0[r]??m0.failover),l.push(""),o.lastPush?.sha){let p=f0(n,o.lastPush.ts),f=p?` (~${p} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${f}`+(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 p=f0(n,i);l.push(`## Last real checkpoint (written by the previous agent${p?`, ~${p} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=_P(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(p=>`\`${p}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(p=>`\`${p}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let p of c)l.push(`**${p.role==="user"?"operator":"agent"}:** ${AP(p.text,xP)}`),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 jP(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(b0.test(s)){if(r.length>=R0)continue;let a=DP(s.replace(b0,"").replace(/[.;,:]+$/,""),O0,NP);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>y0&&(o=`${o.slice(0,y0-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function C0(e,t,{timeoutMs:n=9e4}={}){let r=Er.join(t,CP),o=Er.join(r,MP);await Ar.mkdir(r,{recursive:!0});let s=await Gl(o),a=await ve(e);if(!a)throw new Error(`session "${e}" not found`);return await Kl(e,a,PP(o)),await L0(o,s,n),jP(await Ar.readFile(o,"utf8"))}function M0(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===E0||n===$0;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(A0)||n===$P||r.startsWith(_0)||r.startsWith(I0)}function N0(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-BP;t.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&t.push({box:r.to,from:s,to:o})}let n=e?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&t.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&t.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),t}function P0(e,t){let n=[];for(let r of e?.boxes||[]){let o=t?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function D0(e,{windows:t=[]}={}){if(M0(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 Vl(e){return Of.get(e)||null}async function j0(e){let t=Of.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ve(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:Er.join(n.cwd,v0,S0)};return Of.set(e,r),UP(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function UP(e,t){e.step="summarize",await B0(e.name,t),e.step="clear";let n=await ve(e.name);await Kl(e.name,n,"/clear"),await Nf(LP),e.step="resume",await Kl(e.name,n,OP),e.step="done",e.finishedAt=Date.now()}async function B0(e,t,{timeoutMs:n=FP}={}){let r=Er.join(t,v0),o=Er.join(r,S0);await Ar.mkdir(r,{recursive:!0});let s=await Gl(o),a=await ve(e);await ue({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Kl(e,a,RP(o)),await L0(o,s,n),await ue({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Gl(o)});let i=await Yk(t,o,{exclude:u=>M0(u.role,u.text)});await ue({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await C({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 Ar.readFile(o,"utf8");await C({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...T0(u),ts:Date.now()}})}catch{}}await Cp(t);let l=await HP(r,o);return c&&await Rb(c,l||o,{box:e,agent:a?.agent??null}),o}async function Cf(e,{timeoutMs:t}={}){let n=await ve(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return B0(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Mf(e,t,{trackName:n="this Track"}={}){let r=await G({id:e,cwd:t}),o=h0(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Er.join(t,k0);return await Ar.mkdir(Er.dirname(s),{recursive:!0}),await Ar.writeFile(s,o,"utf8"),await C({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...T0(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),k0}async function Gl(e){try{return(await Ar.stat(e)).mtimeMs}catch{return 0}}async function L0(e,t,n){let r=Date.now()+n;for(;;){if(await Gl(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Nf(2e3)}}async function HP(e,t){try{if(!IP.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Er.join(e,`${n}.md`);return await Ar.copyFile(t,r),r}catch{return null}}function Nf(e){return new Promise(t=>setTimeout(t,e))}function x0(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 Kl(e,t,n){if(t){let o=x0(t.agent);await Tr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ve(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await qe(e,["Escape"],{kind:"drive-wipe"}),await Nf(150),await ks(e,Al,{kind:"drive-wipe"}),await nt(e,n,{kind:"orchestration-drive",...kn(r.agent,n)}),await mi({sessionName:e,agent:r.agent,agentCmd:x0(r.agent),text:n,flow:"orchestration-drive"})}var v0,S0,g0,A0,E0,_0,$P,RP,OP,I0,$0,CP,MP,y0,R0,O0,NP,PP,b0,DP,BP,LP,FP,Of,k0,Pf=q(()=>{dt();ui();yn();di();kf();Jk();Wa();fe();wl();w0();v0=".bostrat/handoff",S0="latest.md",g0=32*1024;A0="Checkpoint: write a work-in-progress handoff",E0="HANDOFF WRITTEN",_0="Resume from .bostrat/handoff/latest.md",$P="/clear",RP=e=>`${A0} 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 ${E0} and take no further action.`,OP=`${_0}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,I0="Beat: write a",$0="BEAT WRITTEN",CP=".bostrat/arc",MP="beat.txt",y0=200,R0=4,O0=5,NP=36,PP=e=>`${I0} 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 ${R0} further lines, each starting with "- " and naming ONE piece in at most ${O0} 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 ${$0} and take no further action.`,b0=/^(?:[-–—*•·]|\d+[.)])\s+/,DP=(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};BP=6e5;LP=3500,FP=3e5,Of=new Map;k0=".bostrat/handoff/status-latest.md"});function Ir(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!zl.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 H0(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:Ir(o.from,"from",n),toMs:Ir(o.to,"to",n)}))}return t}function jf(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 qP(e){if(!e)return null;let t=String(e).trim();return _r[t]?t:F0[t]?F0[t]:Object.keys(_r).find(r=>t.startsWith(r))||null}function GP(e){if(!e||e===_r)return WP;let t=U0.get(e);return t||(t=H0(e),U0.set(e,t)),t}function W0(e,t,n={}){let{table:r,speed:o}=n,s=GP(r),a=r&&r!==_r?s[e]?e:null:qP(e);if(!a)return null;let i=jf(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 vn(e,t={},n,r){let o=W0(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 KP(e=_r){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:Ir(a.from,"from",n),toMs:Ir(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`),zl.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 zP({now:e=Date.now(),table:t=_r,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...VP,...n},a=[],i=jf(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:Ir(d.from,"from",c),toMs:Ir(d.to,"to",c),verifiedMs:zl.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 p=i>=d.fromMs&&i<=d.toMs,f=d.fromMs>i&&d.fromMs-i<=o*Df;p&&i-d.verifiedMs>r*Df&&a.push(`${c}: current rate last verified ${d.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),f&&d.fromMs-d.verifiedMs>s*Df&&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 Os({now:e=Date.now(),table:t=_r,...n}={}){let r=jf(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=Ir(l.from,"from",i),d=Ir(l.to,"to",i);r>=u&&r<=d&&zl.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=[...KP(t),...zP({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var _r,F0,zl,WP,U0,q0,VP,Df,qn=q(()=>{_r={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},F0={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"},zl=/^\d{4}-\d{2}-\d{2}$/;WP=H0(_r);U0=new WeakMap;q0=(e,t,n)=>W0(e,t,n)!==null;VP=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Df=864e5});function YP(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function K0(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Wt(e){let t=e.payload?.model||"unknown",n=YP(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=vn(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&&!q0(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function $r(e,{boxLabels:t={}}={}){let n=(e||[]).filter(p=>p.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=G0();for(let p of n){let{usd:f,model:h,tokens:w,repriced:g,unpriced:k,asRecorded:x}=Wt(p);i+=x;let y=p.provider||"unknown",I=p.boxId||"unattributed";if(g&&c++,k&&(l++,u.add(h)),a+=f,K0(d,w),r[h]||={usd:0,tokens:G0(),events:0},r[h].usd+=f,r[h].events+=1,K0(r[h].tokens,w),o[y]||={usd:0,events:0},o[y].usd+=f,o[y].events+=1,s[I]||={usd:0,events:0,provider:y,models:new Set,label:t[I]||I},s[I].usd+=f,s[I].events+=1,h!=="unknown"&&s[I].models.add(h),p.epoch!=null){let R=(s[I].epochs||={})[p.epoch]||={usd:0,events:0,provider:y,models:new Set};R.usd+=f,R.events+=1,R.provider=y,h!=="unknown"&&R.models.add(h)}}return{totalUsd:Cs(a),asRecordedUsd:Cs(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Yl(r,p=>({...p,usd:Cs(p.usd)})),byProvider:Yl(o,p=>({...p,usd:Cs(p.usd)})),byBox:Yl(s,p=>({usd:Cs(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:Yl(p.epochs,f=>({usd:Cs(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var G0,Cs,Yl,Rr=q(()=>{qn();G0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});Cs=e=>Math.round(e*1e4)/1e4,Yl=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function JP(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 V0(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[p,f]=d.split("#");return`${p.split("/").pop()}#${f}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Bf(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Bf(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(`${Bf(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=$r(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?JP(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Bf,z0=q(()=>{Rr();Bf=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as XP}from"node:child_process";import{promisify as ZP}from"node:util";import{randomUUID as QP}from"node:crypto";async function Lf(e,t){let n=Date.now()+t;for(;;){if(await Un(e))return!0;if(Date.now()>n)return!1;await bi(tD)}}async function rD(e,t){let n=J0[t]||J0.default;n.escape&&(await qe(e,["Escape"],{kind:"swap-exit"}),await bi(150)),n.wipe&&await ks(e,Al,{kind:"swap-exit"}),await El(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=kn(t).submitDelayMs||0;await bi(Math.max(r,250)),await qe(e,["Enter"],{kind:"swap-exit"})}async function X0(e,t){try{let{stdout:n}=await yo(X,Z("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Z0(e){await yo(X,Z("respawn-pane","-k","-t",e))}async function Ff(e,t={}){if(!Fe(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ve(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||kr(n),a=Number(t.exitTimeoutMs)||eD;await ue({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Un(e)&&(await rD(e,o),!await Lf(e,a)&&(i=!0,await ue({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Z0(e),!await Lf(e,Y0))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&po(n),l=c?Xc():null,u=await X0(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await yo(X,Z("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await yo(X,Z("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?bl(g,l):g}if(d&&!i&&(await Z0(e),!await Lf(e,Y0)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let p=!!t.resumeSessionId,f=t.resumeSessionId||QP(),h=p?n:Qa(n,f,e),w=p?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await fr(r.cwd),s==="codex"&&await is(r.cwd),s==="cursor"&&await cs(r.cwd),await fo(e),await yo(X,Z("set-option","-t",e,"@bostrat-sid",f)),await yo(X,Z("set-option","-t",e,"@bostrat-agent",s)),await bi(nD),c&&Za().length&&(await X0(e,Xa)||await yo(X,Z("set-environment","-t",e,Xa,"1")).catch(()=>{}),await nt(e,`export ${Xa}=1`,{kind:"swap-env"}),await bi(150)),await nt(e,h,{kind:"swap-launch"}),await ue({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:f,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:f,agent:s,pinned:w,forced:i,envRespawned:d,launchCmd:h}}var yo,bi,eD,Y0,tD,nD,J0,Q0=q(()=>{dt();Da();Qc();el();di();yn();yo=ZP(XP),bi=e=>new Promise(t=>setTimeout(t,e)),eD=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Y0=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,tD=250,nD=400,J0={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 ex(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await _t(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
156
|
-
`)){let
|
|
157
|
-
`).trim():"",
|
|
158
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(
|
|
159
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
160
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
161
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
162
|
-
`),o=
|
|
163
|
-
`),o=
|
|
164
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
165
|
-
`)].reverse().find(a=>
|
|
166
|
-
`),o=
|
|
167
|
-
`),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match(
|
|
168
|
-
`),o=
|
|
169
|
-
`).pop()||"")?!0
|
|
170
|
-
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function
|
|
171
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
172
|
-
`).filter(Boolean);return t&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function
|
|
173
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
174
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
175
|
-
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
176
|
-
`),n=t.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
177
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function
|
|
178
|
-
`),n=t.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(t[n]),o=[];for(let s=n+1;s<t.length;s++){let a=t[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function ov(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&&!tv.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=nv.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!r1.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ai(e,t,{timeoutMs:n=t1}={}){let r=Date.now()+n;for(;;){let o=await De(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await au(n1)}}async function a1(e,t){return await nt(e,`/model ${t.model}`,kn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function i1(e,t){await Tl(e),await nt(e,"/model",{enter:!1}),await au(250),await qe(e,["Enter"],{kind:"model-switch"});let n=await Ai(e,Xx);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await qe(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=s1(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await nt(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ai(e,i=>Zx(i)||!Xx(i)&&o(i)&&{done:o(i)});if(!s)throw await qe(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?tv.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 qe(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await nt(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ai(e,i=>!Zx(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 c1(){let e=Jx.join(process.env.CURSOR_CONFIG_DIR||Jx.join(e1.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await QD.readFile(e,"utf8"))?.model||null}catch{return null}}async function l1(e,t){await Tl(e),await El(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ai(e,Qx),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 Ai(e,Qx,{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 qe(e,["Down"],{kind:"model-switch"}),await au(250)}if(!n||!s||!o(s)){await qe(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await qe(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 qe(e,["Enter"],{kind:"model-switch"}),await au(800);let a=await c1();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 sv(e,t,n){return e==="codex"?i1(t,n):e==="cursor"?l1(t,n):a1(t,n)}var au,t1,n1,ev,r1,o1,tv,nv,av=q(()=>{dt();di();au=e=>new Promise(t=>setTimeout(t,e)),t1=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,n1=300,ev=[{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"}],r1=new Set([...ev.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"]),o1=[{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"}],tv=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),nv=[{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 cu(e){let t=e||{},n=u1(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function u1(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:bt(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:bt(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:bt(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:bt(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:bt(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:bt(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:bt(e.agent),payload:{...Mr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:bt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Mr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:bt(e.agent),payload:{change:"subtask-resumed",...Mr(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",...Mr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Mr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Mr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:bt(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:bt(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:bt(e.agent),payload:{...Mr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Mr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:bt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function lv(e,t){let n=cu(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;ym(()=>C(r,n))}async function uv(e){if(!(!e?.id||iv.has(e.id))){iv.add(e.id);try{let t=await G(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=cu(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await C(e,o)}}catch{}}}function Mr(e){let{type:t,at:n,...r}=e;return r}function dv(e,t,n){!n?.promptId||gm.get(e.id)===n.promptId||(gm.set(e.id,n.promptId),ym(()=>C(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:bt(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 bm(e,t,n,r={}){gm.delete(e.id),ym(()=>C(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:bt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var bt,iu,ym,cv,iv,gm,km=q(()=>{fe();bt=e=>e||null;iu=Promise.resolve(),ym=e=>(iu=iu.then(e).catch(()=>{}),iu),cv=()=>iu;iv=new Set;gm=new Map});function h1(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function gv(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of h1(n).reverse()){if(!d1.test(r))continue;let o=r.endsWith("?")&&r.length<=m1&&f1.test(r);if(p1.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function bv(e){let t=String(e||""),n=[];w1.test(t)&&n.push("commit"),yv.test(t)&&n.push("rebase"),g1.test(t)&&n.push("push");let r=b1.test(t);!r&&y1.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 kv(e){return yv.test(String(e||""))}function Ws(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(f=>`${f.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 f=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${f} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${f} 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 f=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",h=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${h}${f}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${f}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${h}${f}`:`push this branch${l} to the remote${f}`)}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 p=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(p+=` ${d.join(", and ").replace(/^./,f=>f.toUpperCase())}.`),a.has("pr")&&(p+=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."),p}function xv(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 vv(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<=k1&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Sv(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+x1||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Tv(e){let t=(Array.isArray(e)?e:[]).filter(r=>pv[r]).map(r=>pv[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 fv,mv,hv,wv,d1,p1,f1,m1,Nr,w1,yv,g1,y1,b1,k1,x1,pv,_o=q(()=>{fv=/(?<!\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,mv=/(?<!\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,hv=/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,wv=/(?<!\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,d1=new RegExp(`\\b(?:${fv}|${mv}|${hv}|${wv})\\b`,"i"),p1=/\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,f1=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,m1=90;Nr=["commit","rebase","push","pr"],w1=new RegExp(`\\b(?:${fv})\\b`,"i"),yv=new RegExp(`\\b(?:${wv})\\b`,"i"),g1=new RegExp(`\\b(?:${mv})\\b`,"i"),y1=new RegExp(`\\b(?:${hv})\\b`,"i"),b1=/\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;k1=3e4;x1=2500;pv={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import v1 from"node:crypto";import lu from"node:fs/promises";import Av from"node:path";import S1 from"node:os";async function We(){return Pr||(xm??=lu.readFile(Sm,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Pr=e,!t&&Le()){let{records:n,source:r}=await ot("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;Pr[ie(i.repo,i.number)]=i}Object.keys(Pr).length&&await Tm().catch(()=>{})}}return Pr}).finally(()=>xm=null),xm)}async function Tm(){let e=JSON.stringify({links:Pr},null,2),t=`${Sm}.${process.pid}.tmp`;await lu.mkdir(Ev,{recursive:!0}),await lu.writeFile(t,e),await lu.rename(t,Sm)}async function Xe(e){vm=vm.then(Tm,Tm),await vm.catch(()=>{});let t=Pr?.[e];t&&yt("prlinks",{id:_v(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Ct({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 We(),c=ie(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:T1.has(a),firstSeen:Date.now()},await Xe(ie(e,t)),{link:i[c],created:!0})}async function Kn(e,t){if(!e||t==null)return null;let n=(await We())[ie(e,t)];return!n||n.announced?null:(n.announced=!0,await Xe(ie(e,t)),n)}async function Iv(e,t){if(!e||t==null)return!1;let n=(await We())[ie(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Xe(ie(e,t)),!0):!1}async function st(e,t){return!e||t==null?null:(await We())[ie(e,t)]??null}async function Dr(e,t,n){if(!e||t==null)return null;let r=(await We())[ie(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,delete r.followupPending,await Xe(ie(e,t)),r):null}async function $v(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ie(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ie(e,t)),o):null}async function Rv(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ie(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ie(e,t)),o):null}async function uu(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[ie(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Xe(ie(e,t)),r)}async function du(e,t){if(!e||t==null)return null;let n=(await We())[ie(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Xe(ie(e,t)),n)}async function pu(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[ie(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Xe(ie(e,t)),r)}async function fu(e,t){if(!e||t==null)return null;let n=(await We())[ie(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Xe(ie(e,t)),n)}async function Ov(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await We())[ie(e,t)];return r?(r.baseFoldAt=n,await Xe(ie(e,t)),r):null}async function Cv(e,t){if(!e||t==null)return null;let n=(await We())[ie(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Xe(ie(e,t)),n)}async function Mv(e,t,n){if(!e||t==null||!n)return null;let r=(await We())[ie(e,t)];return r?(r.lastHeadSha=n,await Xe(ie(e,t)),r):null}async function Nv(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await We())[ie(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Xe(ie(e,t))),{prev:o,link:r}}async function Ei(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await We())[ie(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Xe(ie(e,t)),r):null}async function Io(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ie(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Xe(ie(e,t)),r):null}async function $o(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ie(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Xe(ie(e,t)),r):null}async function Pv(e,t,n){if(!e||t==null)return null;let r=(await We())[ie(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Xe(ie(e,t)),r}async function Dv(e,t,n){if(!e||t==null)return null;let r=(await We())[ie(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.followupPending=[...o.values()],await Xe(ie(e,t)),r}async function _i(e,t){if(!e||t==null)return null;let n=(await We())[ie(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Xe(ie(e,t)),n)}async function jv(){return Object.values(await We()).filter(e=>!A1.has(e.state))}async function Vn(){return Object.values(await We()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function Bv(){if(!Le())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await We(),r=0;for(let[o,s]of Object.entries(n))await yt("prlinks",{id:_v(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Ev,Sm,ie,_v,Pr,xm,vm,T1,A1,zn=q(()=>{br();Ev=process.env.BOSTRAT_STATE_DIR||Av.join(process.env.HOME||S1.tmpdir(),".bostrat"),Sm=Av.join(Ev,"pr-links.json"),ie=(e,t)=>`${e}#${Number(t)}`,_v=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=v1.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Pr=null,xm=null,vm=Promise.resolve();T1=new Set(["api","observed","reconciled"]);A1=new Set(["merged","closed","lost"])});import E1 from"node:fs";import Am from"node:fs/promises";import Hv from"node:path";import _1 from"node:os";function Gv(e,t=!1){if(!t&&e-Fv<I1)return;Fv=e;let n=JSON.stringify({windowStart:qs,byCaller:Object.fromEntries(Yn),lastHour:jr});Uv=Uv.then(()=>Am.mkdir(Wv,{recursive:!0})).then(()=>Am.writeFile(`${mu}.tmp`,n)).then(()=>Am.rename(`${mu}.tmp`,mu)).catch(()=>{})}function Kv(e){e-qs<qv||(jr={start:qs,total:hu(Yn),byCaller:Object.fromEntries(Yn)},jr.total&&console.log(`[github] ${jr.total} calls last hour`,jr.byCaller),qs=e,Yn=new Map,Gv(e,!0))}function Jn(e="other",t=Date.now()){Kv(t),Yn.set(e,(Yn.get(e)||0)+1),Gv(t)}function Vv(e=Date.now()){return Kv(e),{windowStart:qs,total:hu(Yn),byCaller:Object.fromEntries(Yn),lastHour:jr,backoffUntil:Ro>e?Ro:null}}function Br(e){return $1.test(String(e?.message||e||""))}function Lr(e,t=Date.now()){return Ro<=t&&console.warn(`[github] rate limited \u2014 backing off ${Lv/6e4} min:`,String(e?.message||e||"").slice(0,200)),Ro=Math.max(Ro,t+Lv),Ro}function wu(e=Date.now()){return Math.max(0,Ro-e)}var Wv,mu,qv,Lv,I1,qs,Yn,jr,Ro,Fv,hu,Uv,$1,Ii=q(()=>{Wv=process.env.BOSTRAT_STATE_DIR||Hv.join(process.env.HOME||_1.tmpdir(),".bostrat"),mu=Hv.join(Wv,"github-metrics.json"),qv=60*6e4,Lv=5*6e4,I1=6e4,qs=Date.now(),Yn=new Map,jr=null,Ro=0,Fv=0,hu=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(E1.readFileSync(mu,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<qv?(qs=e.windowStart,Yn=new Map(Object.entries(e.byCaller||{})),jr=e.lastHour||null):hu(new Map(Object.entries(e.byCaller||{})))&&(jr={start:e.windowStart,total:hu(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Uv=Promise.resolve();$1=/rate limit|too quickly|abuse detection|HTTP 429/i});import zv from"node:fs/promises";import Yv from"node:path";import R1 from"node:os";function yu({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};gu.push(l),gu.length>C1&&gu.shift();let u=Em.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)),Em.set(e,u),M1(l),l}async function M1(e){try{await zv.mkdir(Jv,{recursive:!0}),await zv.appendFile(O1,JSON.stringify(e)+`
|
|
179
|
-
`)}catch{}}function Xv(){let e={};for(let[t,n]of Em)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:gu.slice(-20)}}var Jv,O1,C1,gu,Em,bu=q(()=>{Jv=process.env.BOSTRAT_STATE_DIR||Yv.join(process.env.HOME||R1.tmpdir(),".bostrat"),O1=Yv.join(Jv,"github-latency.ndjson"),C1=100,gu=[],Em=new Map});import{execFile as N1}from"node:child_process";import{promisify as P1}from"node:util";async function D1(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ks("git",["-C",e,"rev-parse","HEAD"]),Ks("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await C({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function qt(e,t,n={}){let{caller:r="prs",...o}=n,s=await wn({cwd:e});Jn(r);try{let{stdout:a}=await Ks("gh",t,{cwd:e,...j1,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Br(a)&&Lr(a),a}}async function $m(){if(ku&&Date.now()-ku.at<6e4)return ku.data;let e;try{Jn("auth");let t=await Ks("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 La();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return ku={at:Date.now(),data:e},e}async function B1(e){try{let{stdout:t}=await Ks("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Qv(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 _m(e){let t=(e||[]).map(Qv),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 Om(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function tS(e,t){if(!t)return null;let n=await _e(e);if(!n)return null;try{let r=await qt(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 nS(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 rS(e,t){let n=await tS(e,t);return n===null?{..._m([]),unavailable:!0}:_m(nS(n))}function xu(e,t=_m(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",state:e.state??null,mergedAt:e.mergedAt??null,closedAt:e.closedAt??null,additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function F1(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Gs.delete(s);continue}r.add(s),Gs.has(s)||Gs.set(s,n),o.mergeabilityPending=n-Gs.get(s)<L1}for(let o of Gs.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Gs.delete(o);return t}async function Vs(e,t){let n=await Xn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await qt(n.path,["pr","view",String(r),"--json",Rm]);return xu(JSON.parse(o))}catch(o){if(!Om(o))throw o;let s=await qt(n.path,["pr","view",String(r),"--json",eS]),a=JSON.parse(s);return xu(a,await rS(n.path,a.headRefOid))}}async function Tn(e,{caller:t="prs"}={}){try{let n=await qt(e,["pr","list","--json",Rm,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>xu(r)).sort(Zv)}catch(n){if(!Om(n))throw n;let r=await qt(e,["pr","list","--json",eS,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>xu(o,await rS(e,o.headRefOid))))).sort(Zv)}}function Gt(){$i.clear()}async function W1(e,t){if(!e)return t;for(let n of t){let r=await st(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await uu(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await pu(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function oS(){let e=await $m();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await Ln())await B1(a.path)&&t.push(a);let n=Date.now(),r=wu(n)>0,o=await Promise.all(t.map(async a=>{let i=$i.get(a.path),c=i?.pending?H1:U1;if(i&&(r||n-i.at<c))return i.entry;try{let l=await _e(a.path).catch(()=>null),u=F1(a.path,await Tn(a.path,{caller:"tab"}),n);if(await W1(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(w=>w.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&yu({change:f.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:f.number,githubAt:f.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},p=u.some(f=>f.mergeabilityPending||f.checks.pending>0||f.autoMergeArmed);return $i.set(a.path,{at:Date.now(),entry:d,pending:p}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:An(l)}}})),s=new Set(t.map(a=>a.path));for(let a of $i.keys())s.has(a)||$i.delete(a);return{auth:e,repos:o}}async function Xn(e){let t=(await Ln()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Oo(e){return ls(e)}async function zs(e,t,{method:n="squash"}={}){let r=await Xn(e);if(!Ri.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 wn({cwd:r.path})).GH_TOKEN){let a=await xp().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await qt(r.path,s)}catch(a){throw new Error(An(a))}finally{Gt()}return{ok:!0,number:o}}async function Oi(e,t){let n=await Xn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await qt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(An(s))}finally{Gt()}let o=await _e(n.path).catch(()=>null);return o&&await Ov(o,r).catch(()=>{}),{ok:!0,number:r}}async function Cm(e,t){let n=await Xn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await qt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(An(o))}finally{Gt()}return{ok:!0,number:r}}function Mm(e){return e.green&&!e.isDraft}function V1(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 sS(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Mm(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function aS(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Ci(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 iS(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Ci))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function cS(){let e={};for(let[t,n]of Im)e[t]={...n};return e}async function lS(e,{method:t="merge",onMerged:n=null}={}){let r=await Xn(e);if(!Ri.has(t))throw new Error(`invalid merge method: ${t}`);if(Im.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Tn(r.path)).filter(Mm).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 Im.set(r.path,s),Y1(s,o,{listPrs:()=>Tn(r.path),merge:a=>zs(r.path,a,{method:t}),sleep:K1,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function z1(e,t){let n=Date.now()+(t.settleMaxMs??G1),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(q1)}}async function Y1(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 z1(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: ${An(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(!Mm(a)){e.skipped.push({number:a.number,title:a.title,reason:V1(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:An(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Gt()}}async function Nm(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ks("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${An(l)}`)}await D1(e);let o=n||"";if((await wn({cwd:e})).GH_TOKEN){let l=await xp().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 qt(e,s)}catch(l){throw new Error(
|
|
152
|
+
`});function QP(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 tD(e,t,n){let r=$0(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 Sn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Map,l=new Set,u=new Map,d=new Map,p=(k,h)=>{d.set(k,h),t.push(h)},f=k=>{let h=d.get(k);h&&(h.retired=!0)},w=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let k of e||[]){if(k.type!=="tool.called"||k.source!=="transcript")continue;let h=String(k.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let v of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let D=v[0].split("/").pop().toLowerCase();m.has(D)||m.set(D,[]),m.get(D).push(k.payload?.ts??k.ts)}}let g=3*6e4,b=20*6e4,x=(k,h)=>!!(k?.name&&m.get(String(k.name).toLowerCase()))?.some(D=>D>=h-g&&D<=h+b);for(let k of e||[])if(k.type==="turn.retracted"){r.add(`${k.boxId}|${k.payload?.saidTs}`);let h=He(k.payload?.text);h&&(o.has(k.boxId)||o.set(k.boxId,[]),o.get(k.boxId).push(h))}else if(k.type==="turn.user"&&k.source==="orchestrator"){let h=He(k.payload?.text);h&&(s.has(k.boxId)||s.set(k.boxId,new Set),s.get(k.boxId).add(h))}else if(k.type==="job.stalled"&&k.payload?.id)i.set(k.payload.id,{ts:he(k),quietMs:Number(k.payload.quietMs)||null});else if(k.type==="job.started"&&k.payload?.id)a.add(k.payload.id),(k.payload.kind||"agent")==="agent"&&k.payload.name&&l.add(k.payload.name);else if(k.type==="turn.user"&&k.payload?.note?.kind==="task"){let h=k.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:he(k)};h.toolUseId&&c.set(h.toolUseId,v);let D=String(h.summary||"").match(w);D&&u.set(D[1],v)}let y=new Map,$=new Map,O=null,M=new Map,H=new Map,P=new Map,E=new Map,R=new Map,G=new Map,V=new Map,A=new Map,_=new Map,N=new Map,U=new Map,z=new Map,ue=new Map,pe=new Map,Se=new Map,Ae=[],W=k=>Ae.push(k),te=k=>{if(!Ae.length)return;let h=he(k);for(let v of Ae){if(v.status!=="open"||v.box&&v.box!==k.boxId||(v.ts??0)>h)continue;let D=t.indexOf(v);D>=0&&t.splice(D,1),v.ts=h,t.push(v)}},ie=new Map,J=new Map,ge=new Map,Oe=new Map,Me=new Map,I=new Map,j=new Map,fe=new Map,Pe=()=>{for(let k of fe.values())k.questions.length&&t.push(k);fe.clear()},Ce=null,wn=6e4,gt=new Map,gr=(k,h)=>{let v=o.get(k);if(!v?.length)return!1;let D=He(h),ce=s.get(k);return v.some(ye=>{if(!D.startsWith(ye))return!1;let De=He(D.slice(ye.length));return!!De&&!!ce?.has(De)})};for(let k of e||[]){let h=k.payload||{};switch(k.type){case"turn.user":{if(k.source==="hook"||(Ae.length=0,!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length))break;{let v=h.note||Ti(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let D=String(v.summary||"").match(w);if(!v.toolUseId&&D&&l.has(D[1]))break}if(v.kind==="command"&&x0(v.command))break;if(v.kind==="resume-summary"){t.push({id:k.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:he(k),box:k.boxId,agent:k.provider});break}v.kind!=="suppress"&&t.push({id:k.id,kind:"event",type:T0(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:he(k),box:k.boxId,agent:k.provider});break}}if(r.has(`${k.boxId}|${he(k)}`)||k.source==="transcript"&&gr(k.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:k.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:he(k),box:k.boxId,agent:k.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==="followup-go"){h.kind==="prompt-answer"&&(Ce=null);break}for(let v of M.values())v.status="stale";M.clear();for(let v of P.values())v.status="stale";P.clear();for(let v of ue.values())v.status="stale";ue.clear(),Ce&&Ce.box===k.boxId&&(t.push(QP(Ce)),Ce=null),t.push({id:k.id,kind:"message",role:"user",text:h.text,ts:he(k),box:k.boxId,agent:k.provider,boxRole:h.boxRole,...k.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 v=h.from||{};t.push({id:k.id,kind:"event",type:"peer-message",text:h.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:he(k),box:k.boxId,agent:k.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let v=Pk(h,{codexFailedText:xo});if(v){t.push({id:k.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:k.provider,legacy:!0,at:he(k),ts:he(k),box:k.boxId,agent:k.provider});break}Pe();let D=(h.images||[]).filter(qe=>!x(qe,he(k)));if(!String(h.text||"").trim()&&!D.length)break;if(k.provider==="cursor"){let qe=`${k.boxId}|${h.text}`;if(n.has(qe))break;n.add(qe)}(!Ce||Ce.box===k.boxId)&&(Ce=null);let ce=k.provider==="codex"?(h.tools||[]).filter(qe=>!hf.has(qe)&&!di.has(qe)):h.tools||[],ye=h.text,De=null,St=Df(String(h.text||""));St&&(ye=St.text,De={questions:St.questions}),t.push({id:k.id,kind:"message",role:"assistant",text:ye,ts:he(k),box:k.boxId,agent:k.provider,boxRole:h.boxRole,...De?{ask:De}:{},...ce.length?{tools:ce}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...D.length?{images:D}:{}}),te(k),De&&(Ce={id:k.id,groupId:jf(k.provider,h.sourceId||k.id,De.questions),provider:k.provider,box:k.boxId,questions:De.questions,ts:he(k)});break}case"job.started":{if(!h.id)break;let v=he(k),D=c.get(h.id);if(!D&&(h.kind||"agent")==="agent"&&h.name){let rt=u.get(h.name);rt&&rt.ts>=v&&(D=rt)}let ce=D?null:i.get(h.id)||null,ye={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":ce?"stalled":"running",startedTs:v,finishedTs:D?.ts??ce?.ts??null,quietMs:ce?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},De=gt.get(k.boxId),St=De?.jobs[De.jobs.length-1]?.startedTs;if(De&&St!=null&&v-St<=wn)De.jobs.push(ye);else{let rt={id:k.id,kind:"job-dock",dockId:h.id,ts:v,box:k.boxId,agent:k.provider,jobs:[ye]};gt.set(k.boxId,rt),t.push(rt)}break}case"handoff.done":t.push(lt(k,"lead-handoff"));break;case"subtask.delegated":t.push({...lt(k,"subtask-delegated"),to:k.boxId,toAgent:k.provider});break;case"subtask.merged":t.push(lt(k,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let D of M.values())D.status="stale";M.clear();for(let D of P.values())D.status="stale";P.clear()}let v=lt(k,`pr-${h.change}`);if(v.box=k.boxId??null,eD.has(h.change)&&h.number!=null){let D=tD(y,$,h);if(y.size>=2){for(let ye of $.values())for(let De of ye)De.grouped=!0;v.groupPrs=[...y.values()];let ce=[v];for(let ye of y.keys())$.set(ye,ce)}else{let ce=$.get(D)??[];ce.push(v),$.set(D,ce)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let D=$0(h),ce=O&&!O.complete||y.size>=2;if(y.has(D))y.delete(D),$.delete(D);else if(h.number!=null)for(let ye of[...y.keys()])ye.endsWith(`#${h.number}`)&&(y.delete(ye),$.delete(ye));if(ce&&h.number!=null){(!O||O.complete)&&(O={id:k.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:he(k),ts:he(k),box:k.boxId,agent:k.provider},t.push(O)),O.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:he(k)}),O.remaining=[...y.values()],O.complete=y.size===0;break}}t.push(v);break}case"pr.offered":{if(!h.offerId)break;for(let D of M.values())D.status="stale";M.clear();for(let D of P.values())D.status="stale";P.clear();let v={id:k.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",source:k.source==="cli"||h.via==="cli"?"cli":"transcript",ts:he(k),box:k.boxId,agent:k.provider};M.set(h.offerId,v),H.set(h.offerId,v),t.push(v),v.source==="cli"&&W(v);break}case"pr.offer.answered":{let v=H.get(h.offerId);v&&(v.status=h.action||"dismiss",v.actions=Array.isArray(h.actions)?h.actions:null,v.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(v.existingPr={number:h.existingPr.number??null}),M.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let D of P.values())D.status="stale";P.clear();let v={id:k.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:he(k),box:k.boxId,agent:k.provider};P.set(h.offerId,v),E.set(h.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=E.get(h.offerId);v&&(v.status=h.choice||"dismiss",P.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let D=lt(k,v);if(v==="model-change"&&(D.to=h.model),v==="node-update"&&D.latest){let ce=t.findIndex(ye=>ye.kind==="event"&&ye.type==="node-update"&&ye.latest===D.latest);if(ce>=0){let ye=t[ce];t[ce]={...D,ts:ye.ts,at:ye.at,firstAt:ye.firstAt??ye.ts,updatedAt:D.ts};break}}t.push(D);break}case"track.transferred":case"track.recovered":t.push(lt(k,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){f(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;p(`view:${h.viewId}`,{id:k.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:he(k),box:k.boxId,agent:k.provider});break}case"view.spinup.failed":{t.push({id:k.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:he(k),box:k.boxId,agent:k.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:k.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:he(k),box:k.boxId,agent:k.provider});break}if(h.change==="deleted"){f(`mock:${h.mockId}`);break}if(h.change!=="created")break;p(`mock:${h.mockId}`,{id:k.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:he(k),box:k.boxId,agent:k.provider});break}case"flow.state.changed":{if(h.change==="deleted"){f(`flow:${h.flowId}`);break}if(h.change!=="created")break;p(`flow:${h.flowId}`,{id:k.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:he(k),box:k.boxId,agent:k.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){f(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;p(`runbook:${h.runbookId}`,{id:k.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:he(k),box:k.boxId,agent:k.provider});break}case"ticket.linked":{t.push({id:k.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:he(k),box:k.boxId,agent:k.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let D of V.values())D.box===(h.box||k.boxId)&&(D.status="stale",V.delete(D.offerId));let v={id:k.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:he(k),box:h.box||k.boxId,agent:h.agent||k.provider};V.set(h.offerId,v),A.set(h.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=A.get(h.offerId);v&&(v.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",V.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let D of R.values())D.key===h.key&&(D.status="stale",R.delete(D.offerId));let v={id:k.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:he(k),box:k.boxId,agent:k.provider};R.set(h.offerId,v),G.set(h.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=G.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",R.delete(h.offerId));break}case"ticket.state.changed":{for(let v of R.values())v.key===h.key&&v.to===h.to&&(v.status="stale",R.delete(v.offerId));if(h.mode==="offer")break;t.push(lt(k,"ticket-state"));break}case"ticket.commented":{t.push({id:k.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:he(k),box:k.boxId,agent:k.provider});break}case"ticket.flow.changed":t.push(lt(k,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let D of _.values())D.status="stale";_.clear();let v={id:k.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:he(k),box:k.boxId,agent:k.provider};_.set(h.proposalId,v),N.set(h.proposalId,v),t.push(v),W(v);break}case"ticket.create.answered":{let v=N.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,_.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let D of U.values())D.status="stale";U.clear();let v={id:k.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:he(k),box:k.boxId,agent:k.provider};U.set(h.proposalId,v),z.set(h.proposalId,v),t.push(v),W(v);break}case"loop.create.answered":{let v=z.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",U.delete(h.proposalId));break}case"followup.armed":{if(!h.armId)break;let v=h.replaces?Se.get(h.replaces):null;v&&v.status==="armed"&&(v.status="replaced");let D={id:k.id,kind:"followup-armed",armId:h.armId,trigger:h.trigger??"now",label:h.label??"",prompt:h.prompt??"",why:h.why??null,status:"armed",ts:he(k),box:k.boxId,agent:k.provider};Se.set(h.armId,D),D.trigger!=="now"&&t.push(D);break}case"followup.armed.cancelled":{let v=Se.get(h.armId);v&&v.status==="armed"&&(v.status="cancelled");break}case"followup.offered":{if(!h.offerId)break;for(let ce of ue.values())ce.trigger===h.trigger&&(ce.status="stale",ue.delete(ce.offerId));let v=Array.isArray(h.rows)?h.rows:[];for(let ce of v){let ye=ce.armId?Se.get(ce.armId):null;ye&&ye.status==="armed"&&(ye.status="offered")}let D={id:k.id,kind:"followup-offer",offerId:h.offerId,trigger:h.trigger??"now",repo:h.repo??null,context:h.context&&typeof h.context=="object"?h.context:{},rows:v.map(ce=>({followupId:ce.followupId??null,armId:ce.armId??null,label:ce.label??"",prompt:ce.prompt??null,promptPreview:ce.promptPreview??null,promptHash:ce.promptHash??null,origin:ce.origin??"operator",why:ce.why??null})),status:"open",answer:null,ts:he(k),box:k.boxId,agent:k.provider};ue.set(h.offerId,D),pe.set(h.offerId,D),t.push(D),D.trigger==="now"&&W(D);break}case"followup.answered":{let v=pe.get(h.offerId);v&&(v.status=h.action==="sent"?"sent":h.action==="removed"?"removed":"dismiss",v.answer={followupId:h.followupId??null,armId:h.armId??null,label:h.label??null,promoted:h.promoted??null,revived:h.revived===!0},ue.delete(h.offerId));break}case"followup.create.offered":{if(!h.proposalId)break;for(let D of ie.values())D.status="stale";ie.clear();let v={id:k.id,kind:"followup-create-offer",proposalId:h.proposalId,label:h.label??"",prompt:h.prompt??"",trigger:h.trigger??"pr.merged",repo:h.repo??null,why:h.why??null,status:"open",created:null,ts:he(k),box:k.boxId,agent:k.provider};ie.set(h.proposalId,v),J.set(h.proposalId,v),t.push(v),W(v);break}case"followup.create.answered":{let v=J.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",ie.delete(h.proposalId));break}case"followup.created":{let v=h.proposalId?J.get(h.proposalId):null;v&&(v.created={followupId:h.followupId??null});break}case"loop.created":{let v=h.proposalId?z.get(h.proposalId):null;v&&(v.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),t.push(lt(k,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let ce of ge.values())ce.loopId===h.loopId&&(ce.status="stale",ce.staleReason="superseded",ge.delete(ce.proposalId));let v=k.type==="loop.update.offered",D={id:k.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...v?{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:he(k),box:k.boxId,agent:k.provider};ge.set(h.proposalId,D),Oe.set(h.proposalId,D),t.push(D),W(D);break}case"loop.update.answered":{let v=Oe.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",ge.delete(h.proposalId));break}case"loop.delete.answered":{let v=Oe.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",ge.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?Oe.get(h.proposalId):null;v&&(v.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),t.push(lt(k,"loop-updated"));break}case"loop.deleted":t.push(lt(k,"loop-deleted"));break;case"loop.paused":t.push(lt(k,"loop-paused"));break;case"planning.change.offered":{if(!h.proposalId||!Array.isArray(h.ops)||!h.ops.length)break;for(let D of Me.values())D.status="stale",D.staleReason="superseded",Me.delete(D.proposalId);let v={id:k.id,kind:"planning-offer",proposalId:h.proposalId,why:h.why??"",ops:h.ops,base:h.base??{},resolved:h.resolved??{},snapped:!!h.snapped,createsMeRow:!!h.createsMeRow,status:"open",staleReason:null,applied:null,ts:he(k),box:k.boxId,agent:k.provider};Me.set(h.proposalId,v),I.set(h.proposalId,v),t.push(v),W(v);break}case"planning.change.answered":{let v=I.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",Me.delete(h.proposalId));break}case"planning.changed":{let v=h.proposalId?I.get(h.proposalId):null;v&&(v.applied={results:Array.isArray(h.results)?h.results:[]}),t.push(lt(k,"planning-changed"));break}case"rework.cause.offered":{if(!h.sha||j.has(h.sha))break;let v={id:k.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,intent:h.intent??null,shas:Array.isArray(h.shas)?h.shas:null,pr:h.pr??null,retracts:0,status:"open",cause:null,ts:he(k),box:k.boxId,agent:k.provider};j.set(h.sha,v),t.push(v);break}case"rework.cause.answered":{let v=h.sha?j.get(h.sha):null;v&&(v.status="answered",v.cause=h.cause??null);break}case"rework.cause.retracted":{let v=h.sha?j.get(h.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},D=h.proposalId?N.get(h.proposalId):null;if(D){D.created=v;break}t.push({id:k.id,kind:"ticket-created",...v,ts:he(k),box:k.boxId,agent:k.provider});break}case"error":{if(!h.kind)break;t.push(lt(k,h.kind));break}case"provider.error":t.push({...lt(k,"provider-error"),provider:k.provider,box:k.boxId,agent:k.provider});break;case"usage.cap.hit":t.push(lt(k,"usage-cap-hit"));break;case"usage.cap.warning":t.push(lt(k,"usage-cap-warning"));break;case"provider.connected":{t.push({id:k.id,kind:"event",type:"provider-connected",provider:h.provider||k.provider,ts:he(k),at:he(k),box:k.boxId,agent:k.provider});break}case"prompt.answered":{if(Ce=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let D=fe.get(v);D||(D={id:k.id,kind:"prompt",groupId:v,provider:k.provider,box:k.boxId,agent:k.provider,questionTotal:h.questionTotal??1,questions:[],ts:he(k)},fe.set(v,D));let ce=h.questionIndex??0,ye={index:ce,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},De=D.questions.findIndex(qe=>qe.index===ce&&He(qe.question)===He(ye.question));De>=0?D.questions[De]=ye:D.questions.push(ye),D.questions.sort((qe,rt)=>qe.index-rt.index),D.questionTotal=Math.max(D.questionTotal,h.questionTotal??1),D.questions.length>=D.questionTotal&&(t.push(D),fe.delete(v));break}default:break}}let gn=d.size?t.filter(k=>!k.retired):t;return rD(gn.sort((k,h)=>(k.ts??0)-(h.ts??0)))}function rD(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<=nD){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 Ff(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:oD(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??Sn(e)).filter(o=>o.kind==="message");return _k(n,r)}function oD(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==$s(e.command)?t:Ir(e.command,e.description||"")}function lt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:he(e),ts:he(e)}}function R0(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)+sD,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&aD.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function O0(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!cD.has(r.type)||!r.box)continue;let o=(r.ts??0)+iD,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 Zl(e){return(e||[]).filter(t=>lD.has(t?.kind)&&t.status==="open")}var he,eD,$0,nD,sD,aD,iD,cD,lD,Ql=q(()=>{Rs();vo();Bl();yi();Xl();Lf();he=e=>e.payload?.ts??e.ts;eD=new Set(["opened","ci-fixed","reconciled","pushed"]),$0=e=>`${e.repo??""}#${e.number}`;nD=2*6e4;sD=12e4,aD=new Set(["archived","unarchived","opened"]);iD=2500,cD=new Set(["pr-opened","pr-merged","pr-auto-armed"]);lD=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","planning-offer","fullscreen-offer","followup-create-offer"])});function mD(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 wD(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 gD(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<pD&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<fD&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function P0(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=mD(e),s=wD(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Sn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-uD);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(N0[r]??N0.failover),l.push(""),o.lastPush?.sha){let p=M0(n,o.lastPush.ts),f=p?` (~${p} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${f}`+(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 p=M0(n,i);l.push(`## Last real checkpoint (written by the previous agent${p?`, ~${p} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=gD(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(p=>`\`${p}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(p=>`\`${p}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let p of c)l.push(`**${p.role==="user"?"operator":"agent"}:** ${hD(p.text,dD)}`),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(`
|
|
153
|
+
`);return d.length<=C0?d:`${d.slice(0,C0)}
|
|
154
|
+
|
|
155
|
+
[\u2026 brief truncated]`}var uD,dD,C0,pD,fD,hD,M0,N0,D0=q(()=>{Ql();uD=12,dD=2e3,C0=48e3,pD=30,fD=10;hD=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},M0=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;N0={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 yD from"node:fs";import Rr from"node:fs/promises";import Or from"node:path";function q0(e){let t=go(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=j0?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,j0).toString("utf8").replace(/�+$/,"")}
|
|
156
|
+
|
|
157
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function _D(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(L0.test(s)){if(r.length>=Y0)continue;let a=ED(s.replace(L0,"").replace(/[.;,:]+$/,""),X0,TD);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>B0&&(o=`${o.slice(0,B0-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Z0(e,t,{timeoutMs:n=9e4}={}){let r=Or.join(t,vD),o=Or.join(r,SD);await Rr.mkdir(r,{recursive:!0});let s=await eu(o),a=await ke(e);if(!a)throw new Error(`session "${e}" not found`);return await tu(e,a,AD(o)),await sx(o,s,n),_D(await Rr.readFile(o,"utf8"))}function Q0(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===G0||n===J0;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(K0)||n===bD||r.startsWith(z0)||r.startsWith(V0)}function ex(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-ID;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 tx(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 nx(e,{windows:t=[]}={}){if(Q0(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 nu(e){return Uf.get(e)||null}async function rx(e){let t=Uf.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ke(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:Or.join(n.cwd,H0,W0)};return Uf.set(e,r),OD(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function OD(e,t){e.step="summarize",await ox(e.name,t),e.step="clear";let n=await ke(e.name);await tu(e.name,n,"/clear"),await qf($D),e.step="resume",await tu(e.name,n,xD),e.step="done",e.finishedAt=Date.now()}async function ox(e,t,{timeoutMs:n=RD}={}){let r=Or.join(t,H0),o=Or.join(r,W0);await Rr.mkdir(r,{recursive:!0});let s=await eu(o),a=await ke(e);await le({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await tu(e,a,kD(o)),await sx(o,s,n),await le({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await eu(o)});let i=await m0(t,o,{exclude:u=>Q0(u.role,u.text)});await le({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await C({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 Rr.readFile(o,"utf8");await C({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...q0(u),ts:Date.now()}})}catch{}}await Wp(t);let l=await CD(r,o);return c&&await Vb(c,l||o,{box:e,agent:a?.agent??null}),o}async function Hf(e,{timeoutMs:t}={}){let n=await ke(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return ox(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Wf(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=P0(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Or.join(t,F0);return await Rr.mkdir(Or.dirname(s),{recursive:!0}),await Rr.writeFile(s,o,"utf8"),await C({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...q0(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),F0}async function eu(e){try{return(await Rr.stat(e)).mtimeMs}catch{return 0}}async function sx(e,t,n){let r=Date.now()+n;for(;;){if(await eu(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await qf(2e3)}}async function CD(e,t){try{if(!yD.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Or.join(e,`${n}.md`);return await Rr.copyFile(t,r),r}catch{return null}}function qf(e){return new Promise(t=>setTimeout(t,e))}function U0(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 tu(e,t,n){if(t){let o=U0(t.agent);await $r({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ke(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ke(e,["Escape"],{kind:"drive-wipe"}),await qf(150),await As(e,Pl,{kind:"drive-wipe"}),await nt(e,n,{kind:"orchestration-drive",...vn(r.agent,n)}),await ki({sessionName:e,agent:r.agent,agentCmd:U0(r.agent),text:n,flow:"orchestration-drive"})}var H0,W0,j0,K0,G0,z0,bD,kD,xD,V0,J0,vD,SD,B0,Y0,X0,TD,AD,L0,ED,ID,$D,RD,Uf,F0,Kf=q(()=>{ct();hi();kn();wi();Rf();h0();Va();de();El();D0();H0=".bostrat/handoff",W0="latest.md",j0=32*1024;K0="Checkpoint: write a work-in-progress handoff",G0="HANDOFF WRITTEN",z0="Resume from .bostrat/handoff/latest.md",bD="/clear",kD=e=>`${K0} 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 ${G0} and take no further action.`,xD=`${z0}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,V0="Beat: write a",J0="BEAT WRITTEN",vD=".bostrat/arc",SD="beat.txt",B0=200,Y0=4,X0=5,TD=36,AD=e=>`${V0} 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 ${Y0} further lines, each starting with "- " and naming ONE piece in at most ${X0} 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 ${J0} and take no further action.`,L0=/^(?:[-–—*•·]|\d+[.)])\s+/,ED=(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};ID=6e5;$D=3500,RD=3e5,Uf=new Map;F0=".bostrat/handoff/status-latest.md"});function Mr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!ru.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 cx(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:Mr(o.from,"from",n),toMs:Mr(o.to,"to",n)}))}return t}function zf(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 ND(e){if(!e)return null;let t=String(e).trim();return Cr[t]?t:ax[t]?ax[t]:Object.keys(Cr).find(r=>t.startsWith(r))||null}function PD(e){if(!e||e===Cr)return MD;let t=ix.get(e);return t||(t=cx(e),ix.set(e,t)),t}function lx(e,t,n={}){let{table:r,speed:o}=n,s=PD(r),a=r&&r!==Cr?s[e]?e:null:ND(e);if(!a)return null;let i=zf(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 Tn(e,t={},n,r){let o=lx(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 DD(e=Cr){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:Mr(a.from,"from",n),toMs:Mr(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`),ru.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 BD({now:e=Date.now(),table:t=Cr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...jD,...n},a=[],i=zf(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:Mr(d.from,"from",c),toMs:Mr(d.to,"to",c),verifiedMs:ru.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 p=i>=d.fromMs&&i<=d.toMs,f=d.fromMs>i&&d.fromMs-i<=o*Gf;p&&i-d.verifiedMs>r*Gf&&a.push(`${c}: current rate last verified ${d.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),f&&d.fromMs-d.verifiedMs>s*Gf&&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 Ps({now:e=Date.now(),table:t=Cr,...n}={}){let r=zf(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=Mr(l.from,"from",i),d=Mr(l.to,"to",i);r>=u&&r<=d&&ru.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=[...DD(t),...BD({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Cr,ax,ru,MD,ix,ux,jD,Gf,Vn=q(()=>{Cr={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},ax={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"},ru=/^\d{4}-\d{2}-\d{2}$/;MD=cx(Cr);ix=new WeakMap;ux=(e,t,n)=>lx(e,t,n)!==null;jD=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Gf=864e5});function LD(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function px(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Wt(e){let t=e.payload?.model||"unknown",n=LD(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=Tn(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&&!ux(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function Nr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(p=>p.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=dx();for(let p of n){let{usd:f,model:w,tokens:m,repriced:g,unpriced:b,asRecorded:x}=Wt(p);i+=x;let y=p.provider||"unknown",$=p.boxId||"unattributed";if(g&&c++,b&&(l++,u.add(w)),a+=f,px(d,m),r[w]||={usd:0,tokens:dx(),events:0},r[w].usd+=f,r[w].events+=1,px(r[w].tokens,m),o[y]||={usd:0,events:0},o[y].usd+=f,o[y].events+=1,s[$]||={usd:0,events:0,provider:y,models:new Set,label:t[$]||$},s[$].usd+=f,s[$].events+=1,w!=="unknown"&&s[$].models.add(w),p.epoch!=null){let O=(s[$].epochs||={})[p.epoch]||={usd:0,events:0,provider:y,models:new Set};O.usd+=f,O.events+=1,O.provider=y,w!=="unknown"&&O.models.add(w)}}return{totalUsd:Ds(a),asRecordedUsd:Ds(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:ou(r,p=>({...p,usd:Ds(p.usd)})),byProvider:ou(o,p=>({...p,usd:Ds(p.usd)})),byBox:ou(s,p=>({usd:Ds(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:ou(p.epochs,f=>({usd:Ds(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var dx,Ds,ou,Pr=q(()=>{Vn();dx=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});Ds=e=>Math.round(e*1e4)/1e4,ou=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function FD(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 fx(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[p,f]=d.split("#");return`${p.split("/").pop()}#${f}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Vf(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Vf(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(`${Vf(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Nr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?FD(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Vf,mx=q(()=>{Pr();Vf=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as UD}from"node:child_process";import{promisify as HD}from"node:util";import{randomUUID as WD}from"node:crypto";async function Jf(e,t){let n=Date.now()+t;for(;;){if(await Kn(e))return!0;if(Date.now()>n)return!1;await Ei(KD)}}async function zD(e,t){let n=wx[t]||wx.default;n.escape&&(await Ke(e,["Escape"],{kind:"swap-exit"}),await Ei(150)),n.wipe&&await As(e,Pl,{kind:"swap-exit"}),await Dl(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=vn(t).submitDelayMs||0;await Ei(Math.max(r,250)),await Ke(e,["Enter"],{kind:"swap-exit"})}async function gx(e,t){try{let{stdout:n}=await To(X,Z("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function yx(e){await To(X,Z("respawn-pane","-k","-t",e))}async function Yf(e,t={}){if(!Be(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ke(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||Ar(n),a=Number(t.exitTimeoutMs)||qD;await le({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Kn(e)&&(await zD(e,o),!await Jf(e,a)&&(i=!0,await le({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await yx(e),!await Jf(e,hx))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&yo(n),l=c?al():null,u=await gx(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await To(X,Z("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await To(X,Z("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?$l(g,l):g}if(d&&!i&&(await yx(e),!await Jf(e,hx)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let p=!!t.resumeSessionId,f=t.resumeSessionId||WD(),w=p?n:oi(n,f,e),m=p?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(w);return c&&await yr(r.cwd),s==="codex"&&await ps(r.cwd),s==="cursor"&&await fs(r.cwd),await bo(e),await To(X,Z("set-option","-t",e,"@bostrat-sid",f)),await To(X,Z("set-option","-t",e,"@bostrat-agent",s)),await Ei(GD),c&&ri().length&&(await gx(e,ni)||await To(X,Z("set-environment","-t",e,ni,"1")).catch(()=>{}),await nt(e,`export ${ni}=1`,{kind:"swap-env"}),await Ei(150)),await nt(e,w,{kind:"swap-launch"}),await le({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:f,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:f,agent:s,pinned:m,forced:i,envRespawned:d,launchCmd:w}}var To,Ei,qD,hx,KD,GD,wx,bx=q(()=>{ct();Ua();cl();ll();wi();kn();To=HD(UD),Ei=e=>new Promise(t=>setTimeout(t,e)),qD=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,hx=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,KD=250,GD=400,wx={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"}}});function ZD(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return kx.test(String(e.content||""))?String(e.content):null;if(e.type==="attachment"){let n=e.attachment;return n?.type==="queued_command"&&n.commandMode==="task-notification"&&typeof n.prompt=="string"?n.prompt:null}let t=e.message?.content;return typeof t=="string"&&kx.test(t)?t:null}async function xx(e,t={}){return(await Sx(e,t)).launches}async function vx(e,t={}){let{launches:n,done:r}=await Sx(e,t);return n.filter(o=>!r.has(o.id))}async function Sx(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await _t(e,t)}catch{return{launches:[],done:new Set}}let o=new Map,s=new Set,a=new Set,i=new Map;for(let l of r.split(`
|
|
158
|
+
`)){let u=l.trim();if(!u||u[0]!=="{")continue;let d;try{d=JSON.parse(u)}catch{continue}if(d.isSidechain)continue;let p=ZD(d);if(p){let m=p.match(XD)?.[1]?.trim();m&&a.add(m);continue}let f=d.message?.content;if(!Array.isArray(f))continue;let w=d.timestamp?Date.parse(d.timestamp):null;for(let m of f)if(!(!m||typeof m!="object")){if(m.type==="tool_use"&&m.id){if(m.name==="Agent"){let g=m.input||{};o.set(m.id,{id:m.id,kind:"agent",name:String(g.description||"").trim()||null,detail:String(g.subagent_type||"").trim()||null,ts:w,proven:!1})}else if(m.name==="Bash"){let g=String(m.input?.command||"").trim(),b={id:m.id,kind:"command",name:String(m.input?.description||"").trim()||(g?$s(g):null),detail:g?$s(g):null,ts:w,proven:m.input?.run_in_background===!0};b.proven?o.set(m.id,b):i.set(m.id,b)}}else if(m.type==="tool_result"&&m.tool_use_id){let g=QD(m.content);if(VD.test(g)&&s.add(m.tool_use_id),g.match(JD)){let x=i.get(m.tool_use_id);x&&(i.delete(m.tool_use_id),x.proven=!0,x.outputFile=g.match(YD)?.[1]?.replace(/\.$/,"")||null,o.set(x.id,x))}}}}let c=[];for(let l of o.values()){if(!l.proven&&!s.has(l.id))continue;let{proven:u,...d}=l;c.push(d)}return{launches:c.slice(-n),done:a}}var VD,JD,YD,kx,XD,QD,Tx=q(()=>{nn();Rs();VD=/^Async (?:agent|task) launched/i,JD=/^Command did not complete within its [^\n]*?moved to the background \(ID: ([^)]+)\)/i,YD=/Output is being written to:\s*(\S+)/i,kx=/^\s*<task-notification>/i,XD=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i;QD=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 Dr(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 e1(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 Ax(e,t,n){switch(e){case"Shell":{let r=String(t?.command||"").trim();return r?{command:r,description:String(t?.description||"").trim()}:null}case"Read":{let r=t?.path;if(!r)return null;let o=Number(t?.offset)||null,s=o&&Number(t?.limit)?o+Number(t.limit)-1:null;return{command:rn(`read ${Dr(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${js(r)}`:`Read ${js(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Dr(t.path,n):null,s=t?.glob?` --glob ${Xf(t.glob)}`:"",a=rn(String(r),40);return{command:rn(`grep ${Xf(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?Dr(t.target_directory,n):null;return{command:rn(`glob ${Xf(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${rn(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:rn(`edit ${Dr(t.path,n)}`,140),description:`Edit ${js(t.path)}`}:null;case"Write":return t?.path?{command:rn(`write ${Dr(t.path,n)}`,140),description:`Write ${js(t.path)}`}:null;case"Delete":return t?.path?{command:rn(`delete ${Dr(t.path,n)}`,140),description:`Delete ${js(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Dr(s,n)).filter(Boolean);return{command:rn(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=e1(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:rn(`apply_patch ${Dr(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${js(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:rn(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var su,rn,js,Xf,Ex=q(()=>{su=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),rn=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,js=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);Xf=e=>`"${String(e).replace(/"/g,'\\"')}"`});import _i from"node:fs";import t1 from"node:fs/promises";import Ao from"node:path";import n1 from"node:os";import r1 from"node:crypto";import o1 from"node:zlib";function $x(e){let t=r1.createHash("md5").update(String(e)).digest("hex");return Ao.join(s1,t)}function Rx(e){try{let t=JSON.parse(_i.readFileSync(Ao.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 a1(e){let t=$x(e),n;try{n=_i.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Ao.join(t,o.name),a=Ao.join(s,"store.db");if(!_i.existsSync(a))continue;let i=Rx(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 au(e,t){if(!e)return null;if(t){let n=Ao.join($x(e),t),r=Ao.join(n,"store.db");if(_i.existsSync(r))return{id:t,dir:n,store:r,...Rx(n)||{createdAtMs:null,updatedAtMs:null}}}return a1(e)}function _x(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function i1(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=o1.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function c1(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 l1(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!su.has(r.toolName))continue;let o=Ax(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 Zf(e,t){if(!Ix)return Bs;let{DatabaseSync:n}=Ix,r;try{r=new n(e,{readOnly:!0})}catch{return Bs}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 Bs;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Bs}let a=s.lastUsedModel||null,i=s.latestRootBlobId?_x(r,s.latestRootBlobId):null;if(!i)return{...Bs,model:a};let c=[],l=[],u=[],d=null,p=[];for(let f of c1(i)){let w=i1(_x(r,f));if(!w)continue;let m=w.providerOptions?.cursor;if(m?.requestId&&p.push(m.requestId),m?.modelProviderMessageId&&p.push(m.modelProviderMessageId),w.role!=="assistant")continue;if(Array.isArray(w.content))for(let x of w.content){let y=x?.providerOptions?.cursor?.modelName;y&&(d=y)}l.push(...l1(w.content,t).map((x,y)=>({...x,id:x.id||`${f}:${y}`}))),u.push(...d1(w.content));let g=u1(w.content);if(!g)continue;let b=[...new Set(u)];u=[],c.push({id:f,text:g,tools:b.length?b:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:p}}catch{return Bs}finally{try{r.close()}catch{}}}function Ox(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(_i.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 Cx(e,t,{limit:n=80}={}){let r=au(e,t);if(!r)return[];let{turns:o,model:s}=await Zf(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=Ox(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 Mx(e,t){let n=au(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Zf(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 Nx(e,t,{limit:n=80}={}){let r=au(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await Zf(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=Ox(r,a.length||1),l=[];a.forEach((u,d)=>{for(let p of u.commands)l.push({...p,summary:Ir(p.command,p.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Ir(u.command,u.description),ts:i+1});return l}async function Px(e,t){let n=au(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await t1.stat(n.store)).mtimeMs)}catch{return null}}var s1,u1,d1,Ix,Bs,Qf=q(()=>{Rs();Ex();s1=process.env.BOSTRAT_CURSOR_CHATS||Ao.join(n1.homedir(),".cursor","chats");u1=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(`
|
|
159
|
+
`).trim():"",d1=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],Ix=process.getBuiltinModule?.("node:sqlite"),Bs={turns:[],trailingCommands:[],model:null,requestIds:[]}});import p1 from"node:fs/promises";function rm(e){return Dx.set(e.agent,e),e}function jr(e){return Dx.get(e)||f1}async function om(e,t={}){return(await jr(e.agent).readMessages(e,t)).map(iu(e))}async function jx(e,t={}){return(await jr(e.agent).readCommands(e,t)).map(iu(e))}async function Bx(e,t={}){return(await jr(e.agent).readJobs(e,t)).map(iu(e))}async function Lx(e,t={}){return(await jr(e.agent).readOpenJobs(e,t)).map(iu(e))}var Ls,em,tm,nm,f1,Dx,iu,sm=q(()=>{nn();Ll();Rs();Tx();Qf();vo();Ls=class{constructor(t,n=new Set){this.agent=t,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async readOpenJobs(){return[]}async updatedAt(){return null}},em=class extends Ls{transcript(t){return!t?.cwd||!t?.sessionId?null:tn(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?gf(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?Ek(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?xx(r,n):[]}async readOpenJobs(t,n){let r=this.transcript(t);return r?vx(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await p1.stat(n)).mtimeMs)}catch{return null}}},tm=class extends Ls{async readMessages(t,n){return Cx(t.cwd,t.sessionId,n)}async readCommands(t,n){return Nx(t.cwd,t.sessionId,n)}async updatedAt(t){return Px(t.cwd,t.sessionId)}},nm=class extends Ls{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Rk(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Ok(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Ck(t.cwd,this.sessionId(t))}},f1=new Ls("unknown"),Dx=new Map;rm(new em("claude",pf));rm(new tm("cursor",su));rm(new nm("codex",di));iu=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Io(e){let t=e.search(lm);return(t>=0?e.slice(0,t):e).trim()}function m1(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function h1(e){let t=String(e).trim();return!!(!t||_o.test(e)||lm.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function w1(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=g1(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function g1(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(p=>p.length>3&&n.includes(p)).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 Ux(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function y1(e,t=""){let n=String(e);if(!Wx.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
160
|
+
`),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(Eo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!_o.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:Io(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 k1(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
161
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!qx.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(Eo);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&&!(b1.test(n[c])||_o.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Io(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 x1(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Eo);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 S1(e,t=""){let n=String(e);if(!am.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(v1.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(Eo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!_o.test(r[u])&&a.push(p)}}if(s.length<2||!s.some(u=>am.test(u.label)))return null;let i=s.map(u=>({label:Io(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 um(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(Eo);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 T1(e,t=""){let n=String(e);if(!Kx.test(n)||!Gx.test(n)||$o.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
164
|
+
`),o=um(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:Io(s.label),description:""}))}]}:null}function A1(e,t=""){let n=String(e);if(!zx.test(n)||$o.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
165
|
+
`),o=um(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Io(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 _1(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!E1.test(o)||dm.test(e[r]))break;n+=o}return n}function I1(e,t=""){let n=String(e);if(!dm.test(n)||$o.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=_1(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 R1(e,t=""){let n=String(e);if(!im.test(n)||!$1.test(n)||$o.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
167
|
+
`)].reverse().find(a=>im.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 O1(e,t=""){let n=String(e);if(!Vx.test(n)||$o.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
|
+
`),o=um(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:Io(s.label),description:""}))}]}:null}function C1(e,t=""){let n=String(e);if($o.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
169
|
+
`),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(Jx);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(_o.test(r[l])||lm.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 M1(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(cm.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 N1(e,t=""){let n=String(e);if(!Hx.test(n)||!cm.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
170
|
+
`),o=M1(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let P=i;P<c;P++){let E=r[P].match(Eo);E&&(u<0&&(u=P),l.push({line:P,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 P of l)for(let E=P.line+1;E<Math.min(r.length,P.line+4)&&!(Eo.test(r[E])||_o.test(r[E]));E++){let R=r[E].trim();if(!(!R||h1(r[E])||Fx.test(R))){P.desc=m1(R);break}}let d=l.filter(P=>!Fx.test(P.label));if(!d.length)return null;let p=d.some(P=>P.hasCheckbox),f=Math.max(0,d.findIndex(P=>P.cursor)),w=s>=0?s+1:0,m=[];for(let P=w;P<u;P++){let E=r[P].trim();E&&!_o.test(r[P])&&!/[☐☒]/.test(r[P])&&m.push(E)}let g=m.join(" ")||(s>=0?Ux(r[s]):"")||"The agent is asking a question",b=s>=0?w1(r[s],g):null,x=b?.currentTabLabel||(s>=0?Ux(r[s]):""),y=d.map(P=>({label:Io(P.label),description:P.desc})),$=y.map(P=>P.label).join("|"),M=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${x}:${g}:${$}`.slice(0,240),H=b?`claude:ask:${b.tabs.map(P=>P.label).join("|")}`.slice(0,240):M;return{provider:"claude",box:t,multiSelect:p,cursorIndex:f,promptId:M,groupId:H,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:g,header:x,multiSelect:p,options:y}]}}function Ro(e,t){let n=String(t||"");return n?e==="claude"?Hx.test(n)&&cm.test(n)||Wx.test(n)||am.test(n)||qx.test(n.trimEnd().split(`
|
|
171
|
+
`).pop()||"")?!0:$o.test(n)?!1:Kx.test(n)&&Gx.test(n)||zx.test(n)||dm.test(n)||im.test(n)||Vx.test(n)||Jx.test(n.trimEnd().split(`
|
|
172
|
+
`).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"?x1(t,e.name)||N1(t,e.name)||y1(t,e.name)||S1(t,e.name)||R1(t,e.name)||I1(t,e.name)||A1(t,e.name)||T1(t,e.name)||O1(t,e.name)||C1(t,e.name)||k1(t,e.name):e.agent==="cursor"||e.agent==="codex"?zk(e.agent,t,e.name):null}async function pm(e){if(!e)return null;let t=await Gn(e.name).catch(()=>"");return Br(e,t)}function Yx(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 Xx(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 Hx,cm,Eo,Fx,_o,lm,Wx,qx,b1,am,v1,$o,Kx,Gx,zx,dm,E1,im,$1,Vx,Jx,fm=q(()=>{ct();Ul();Hx=/to navigate\b.*\bEsc to cancel|Enter to select\b/,cm=/Chat about this/i,Eo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Fx=/^(type something|chat about this|submit)\b/i,_o=/^[─—-]{6,}\s*$/,lm=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;Wx=/^\s*Switch model\?\s*$/m;qx=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,b1=/^\s*[─—▔-]{6,}\s*$/;am=/Resume from summary/i,v1=/^\s*This session is\b/;$o=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;Kx=/Choose the text style that looks best with your terminal/,Gx=/Let's get started\./;zx=/Select login method:/;dm=/Paste code here if prompted/,E1=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;im=/OAuth error:/,$1=/Press Enter to retry\./;Vx=/Quick safety check: Is this a project you created or one you trust/;Jx=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function cu(e,t,n){if(!e||!t)return null;for(let r of P1)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function j1(e){let t=mm.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},mm.set(e,t)),t}function ev(e,t,n=Date.now()){let r=j1(e);if(r.sentAt=r.sentAt.filter(s=>n-s<Qx),r.sentAt.length>=Zx)return{action:"give-up"};if(n-r.last<D1)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 tv(e,t=Date.now()){let n=mm.get(e);return n?n.sentAt.filter(r=>t-r<Qx).length>=Zx:!1}var P1,D1,Zx,Qx,mm,hm=q(()=>{P1=[{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||""}
|
|
173
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];D1=5e3,Zx=5,Qx=3600*1e3,mm=new Map});function Fs(e){return typeof e=="string"?e.trim():""}var nv=q(()=>{});import{execFile as B1}from"node:child_process";import{promisify as L1}from"node:util";async function ze(e,t,{input:n}={}){let r=gm("git",["-C",e,...t],{maxBuffer:rv});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Us(e){if(!e)return!1;try{let{stdout:t}=await ze(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function ov(e){let{stdout:t}=await ze(e,["rev-parse","--show-toplevel"]);return t.trim()}async function F1(e){try{return await ze(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Hs(e,t="bostrat: subtask snapshot"){let{stdout:n}=await ze(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>gm("git",["-C",e,...a],{maxBuffer:rv,env:o,...i});try{let a=await F1(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 gm("rm",["-f",r])}catch{}}}async function ym(e,t,{trailers:n=[]}={}){let{stdout:r}=await ze(e,["status","--porcelain"]);return r.trim()?(await ze(e,["add","-A"]),await ze(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function Ii(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await ze(e,["status","--porcelain"]),r=n.split(`
|
|
174
|
+
`).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 $i(e,t){if(!t)return 0;try{let{stdout:n}=await ze(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Ws(e,t,n,r){return await ze(e,["worktree","add","--no-track","-b",n,t,r]),t}async function sv(e,t){try{return await ze(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function lu(e,t,n){return await ze(e,["worktree","add","--end-of-options",t,n]),t}async function qs(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await ze(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 ze(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function bm(e){await ze(e,["init","-q"]),await ze(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Ot(e,t){try{await ze(e,["worktree","remove","--force",t])}catch{try{await ze(e,["worktree","prune"])}catch{}}}function U1(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 km(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function xm(e,t,n,r){let{stdout:o}=await ze(e,km(["diff","--shortstat",t,n],r));return U1(o)}async function vm(e,t,n,r){let{stdout:o}=await ze(e,km(["diff","--name-only",t,n],r));return o.split(`
|
|
175
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Sm(e,t,n,r){let{stdout:o}=await ze(e,km(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await wm(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await wm(e,o,["--3way","--whitespace=nowarn"]),a=await H1(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await wm(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function wm(e,t,n){try{return await ze(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function H1(e){try{let{stdout:t}=await ze(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
176
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var gm,rv,uu=q(()=>{gm=L1(B1),rv=64*1024*1024});function du(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!W1.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 Ve,W1,An=q(()=>{Ve=e=>!!(e&&(e.archivedAt||e.transferredTo)),W1=new Set(["merged","conflict","canceled","failed","handed-off"])});import Tm from"node:fs/promises";import iv from"node:path";import q1 from"node:os";function dv(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function pv(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function fv(e){let t=new Date(dv(e));return t.setDate(t.getDate()+7),t.getTime()}function mv(e){let t=new Date(pv(e));return t.setMonth(t.getMonth()+1),t.getTime()}function fu(e=Date.now()){for(let[t,n]of Object.entries(Ks)){let r=n.startOf(e);if(r!==Jn[t]){Jn[t]=r;for(let o of Oo.values())o[t]=0;for(let o of Gs)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Gs.delete(o)}}}function hv(e,t){let n=on?.caps?.[e]?.[Ks[t].capKey];return Number.isFinite(n)&&n>0?n:null}function Co(e,t=Date.now()){if(!e||!Em||!on?.caps)return null;fu(t);let n=Oo.get(e);for(let r of Object.keys(Ks)){let o=hv(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:pu(s),resetsAt:Ks[r].nextStart(t)}}return null}function wv(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${Jn[t.window]}`;return Gs.has(n)?!1:(Gs.add(n),!0)}function av(e,t,n,r=Date.now()){if(!e||!t)return;fu(r);let o=K1(e);for(let[s,a]of Object.entries(Ks))n>=Jn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function G1(e,t,n=Date.now()){if(!on?.caps||!Am)return;let r=Oo.get(e);if(r)for(let[o,s]of Object.entries(Ks)){let a=hv(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*uv||i>=a)continue;let c=`warn:${e}:${o}:${Jn[o]}`;if(!Gs.has(c)){Gs.add(c);try{let l=await Am(t);if(!l)continue;await C(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:pu(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function gv(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 mu(e,{source:t="local",updatedAt:n=Date.now()}={}){on={caps:gv(e),updatedAt:n,source:t};try{await Tm.mkdir(cv,{recursive:!0}),await Tm.writeFile(lv(),JSON.stringify(on,null,2))}catch{}return Ri()}function Ri(e=Date.now()){fu(e);let t=new Set([...Oo.keys(),...Object.keys(on?.caps||{})]),n={};for(let r of t){let o=Oo.get(r);n[r]={weeklyUsd:pu(o?.weekly||0),monthlyUsd:pu(o?.monthly||0)}}return{caps:on?.caps||{},configured:!!on,seeded:Em,updatedAt:on?.updatedAt??null,spent:n,windows:{weekly:{start:Jn.weekly,resetsAt:fv(e)},monthly:{start:Jn.monthly,resetsAt:mv(e)}},warnPct:uv}}async function yv({listTracks:e,getTrack:t}={}){Am=t||null,fu();try{let o=JSON.parse(await Tm.readFile(lv(),"utf8"));on={caps:gv(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{on=null}let n=Date.now();ot.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Wt(o);av(o.provider||"unknown",s,o.ts??Date.now()),G1(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Jn.weekly,Jn.monthly);for(let o of await e?.()||[]){let s;try{s=await K(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||av(a.provider||"unknown",Wt(a).usd,i,n)}}return Em=!0,Ri()}var cv,lv,uv,Ks,on,Em,Jn,Oo,Gs,Am,K1,pu,hu=q(()=>{de();Pr();cv=process.env.BOSTRAT_STATE_DIR||iv.join(process.env.HOME||q1.tmpdir(),".bostrat"),lv=()=>iv.join(cv,"usage-caps.json"),uv=.8;Ks={weekly:{startOf:dv,nextStart:fv,capKey:"weeklyUsd"},monthly:{startOf:pv,nextStart:mv,capKey:"monthlyUsd"}},on=null,Em=!1,Jn={weekly:0,monthly:0},Oo=new Map,Gs=new Set,Am=null,K1=e=>{let t=Oo.get(e);return t||(t={weekly:0,monthly:0},Oo.set(e,t)),t};pu=e=>Math.round(e*1e4)/1e4});import z1 from"node:fs/promises";import V1 from"node:os";import bv from"node:path";function Ev(e){return e==="codex"?Z1:e==="cursor"?Av:Sv}function kv(e){let t=String(e||"").split(`
|
|
177
|
+
`),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 xv(e){let t=String(e||"").split(`
|
|
178
|
+
`),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 Q1(e){let t=String(e||"").split(`
|
|
179
|
+
`);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 vv(e){let t=String(e||"").split(`
|
|
180
|
+
`),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 _v(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&&!Tv.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=Av.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!X1.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Oi(e,t,{timeoutMs:n=J1}={}){let r=Date.now()+n;for(;;){let o=await je(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await wu(Y1)}}async function ej(e,t){return await nt(e,`/model ${t.model}`,vn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function tj(e,t){await Nl(e),await nt(e,"/model",{enter:!1}),await wu(250),await Ke(e,["Enter"],{kind:"model-switch"});let n=await Oi(e,kv);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ke(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=Q1(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await nt(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Oi(e,i=>xv(i)||!kv(i)&&o(i)&&{done:o(i)});if(!s)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?Tv.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 Ke(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await nt(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Oi(e,i=>!xv(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 nj(){let e=bv.join(process.env.CURSOR_CONFIG_DIR||bv.join(V1.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await z1.readFile(e,"utf8"))?.model||null}catch{return null}}async function rj(e,t){await Nl(e),await Dl(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Oi(e,vv),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 Oi(e,vv,{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 Ke(e,["Down"],{kind:"model-switch"}),await wu(250)}if(!n||!s||!o(s)){await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ke(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 Ke(e,["Enter"],{kind:"model-switch"}),await wu(800);let a=await nj();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 Iv(e,t,n){return e==="codex"?tj(t,n):e==="cursor"?rj(t,n):ej(t,n)}var wu,J1,Y1,Sv,X1,Z1,Tv,Av,$v=q(()=>{ct();wi();wu=e=>new Promise(t=>setTimeout(t,e)),J1=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,Y1=300,Sv=[{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"}],X1=new Set([...Sv.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"]),Z1=[{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"}],Tv=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Av=[{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 yu(e){let t=e||{},n=oj(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function oj(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:bt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{},...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:bt(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:bt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole,...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:bt(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:bt(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:bt(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:bt(e.agent),payload:{...Lr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:bt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Lr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:bt(e.agent),payload:{change:"subtask-resumed",...Lr(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",...Lr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Lr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Lr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:bt(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:bt(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:bt(e.agent),payload:{...Lr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Lr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:bt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function Cv(e,t){let n=yu(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Im(()=>C(r,n))}async function Mv(e){if(!(!e?.id||Rv.has(e.id))){Rv.add(e.id);try{let t=await K(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=yu(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await C(e,o)}}catch{}}}function Lr(e){let{type:t,at:n,...r}=e;return r}function Nv(e,t,n){!n?.promptId||_m.get(e.id)===n.promptId||(_m.set(e.id,n.promptId),Im(()=>C(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:bt(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 $m(e,t,n,r={}){_m.delete(e.id),Im(()=>C(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:bt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var bt,gu,Im,Ov,Rv,_m,Rm=q(()=>{de();bt=e=>e||null;gu=Promise.resolve(),Im=e=>(gu=gu.then(e).catch(()=>{}),gu),Ov=()=>gu;Rv=new Set;_m=new Map});function lj(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Fv(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of lj(n).reverse()){if(!sj.test(r))continue;let o=r.endsWith("?")&&r.length<=cj&&ij.test(r);if(aj.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function Hv(e){let t=String(e||""),n=[];uj.test(t)&&n.push("commit"),Uv.test(t)&&n.push("rebase"),dj.test(t)&&n.push("push");let r=fj.test(t);!r&&pj.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 Wv(e){return Uv.test(String(e||""))}function zs(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(f=>`${f.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 f=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${f} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${f} 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 f=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",w=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${w}${f}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${f}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${w}${f}`:`push this branch${l} to the remote${f}`)}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 p=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(p+=` ${d.join(", and ").replace(/^./,f=>f.toUpperCase())}.`),a.has("pr")&&(p+=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."),p}function qv(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 Kv(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<=mj&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Gv(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+hj||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function zv(e){let t=(Array.isArray(e)?e:[]).filter(r=>Pv[r]).map(r=>Pv[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 Dv,jv,Bv,Lv,sj,aj,ij,cj,Fr,uj,Uv,dj,pj,fj,mj,hj,Pv,Mo=q(()=>{Dv=/(?<!\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,jv=/(?<!\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,Bv=/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,Lv=/(?<!\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,sj=new RegExp(`\\b(?:${Dv}|${jv}|${Bv}|${Lv})\\b`,"i"),aj=/\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,ij=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,cj=90;Fr=["commit","rebase","push","pr"],uj=new RegExp(`\\b(?:${Dv})\\b`,"i"),Uv=new RegExp(`\\b(?:${Lv})\\b`,"i"),dj=new RegExp(`\\b(?:${jv})\\b`,"i"),pj=new RegExp(`\\b(?:${Bv})\\b`,"i"),fj=/\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;mj=3e4;hj=2500;Pv={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import wj from"node:crypto";import bu from"node:fs/promises";import Vv from"node:path";import gj from"node:os";async function We(){return Ur||(Om??=bu.readFile(Mm,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Ur=e,!t&&Fe()){let{records:n,source:r}=await st("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;Ur[se(i.repo,i.number)]=i}Object.keys(Ur).length&&await Nm().catch(()=>{})}}return Ur}).finally(()=>Om=null),Om)}async function Nm(){let e=JSON.stringify({links:Ur},null,2),t=`${Mm}.${process.pid}.tmp`;await bu.mkdir(Jv,{recursive:!0}),await bu.writeFile(t,e),await bu.rename(t,Mm)}async function Xe(e){Cm=Cm.then(Nm,Nm),await Cm.catch(()=>{});let t=Ur?.[e];t&&yt("prlinks",{id:Yv(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Ct({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 We(),c=se(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:yj.has(a),firstSeen:Date.now()},await Xe(se(e,t)),{link:i[c],created:!0})}async function Yn(e,t){if(!e||t==null)return null;let n=(await We())[se(e,t)];return!n||n.announced?null:(n.announced=!0,await Xe(se(e,t)),n)}async function Xv(e,t){if(!e||t==null)return!1;let n=(await We())[se(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Xe(se(e,t)),!0):!1}async function at(e,t){return!e||t==null?null:(await We())[se(e,t)]??null}async function Hr(e,t,n){if(!e||t==null)return null;let r=(await We())[se(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,delete r.followupPending,await Xe(se(e,t)),r):null}async function Zv(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[se(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(se(e,t)),o):null}async function Qv(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[se(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(se(e,t)),o):null}async function ku(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[se(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Xe(se(e,t)),r)}async function xu(e,t){if(!e||t==null)return null;let n=(await We())[se(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Xe(se(e,t)),n)}async function vu(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[se(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Xe(se(e,t)),r)}async function Su(e,t){if(!e||t==null)return null;let n=(await We())[se(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Xe(se(e,t)),n)}async function eS(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await We())[se(e,t)];return r?(r.baseFoldAt=n,await Xe(se(e,t)),r):null}async function tS(e,t){if(!e||t==null)return null;let n=(await We())[se(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Xe(se(e,t)),n)}async function nS(e,t,n){if(!e||t==null||!n)return null;let r=(await We())[se(e,t)];return r?(r.lastHeadSha=n,await Xe(se(e,t)),r):null}async function rS(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await We())[se(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(se(e,t))),{prev:o,link:r}}async function Ci(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await We())[se(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Xe(se(e,t)),r):null}async function No(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[se(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Xe(se(e,t)),r):null}async function Po(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[se(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Xe(se(e,t)),r):null}async function oS(e,t,n){if(!e||t==null)return null;let r=(await We())[se(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(se(e,t)),r}async function sS(e,t,n){if(!e||t==null)return null;let r=(await We())[se(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.followupPending=[...o.values()],await Xe(se(e,t)),r}async function Mi(e,t){if(!e||t==null)return null;let n=(await We())[se(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(se(e,t)),n)}async function aS(){return Object.values(await We()).filter(e=>!bj.has(e.state))}async function Xn(){return Object.values(await We()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function iS(){if(!Fe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await st("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await We(),r=0;for(let[o,s]of Object.entries(n))await yt("prlinks",{id:Yv(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Jv,Mm,se,Yv,Ur,Om,Cm,yj,bj,Zn=q(()=>{Tr();Jv=process.env.BOSTRAT_STATE_DIR||Vv.join(process.env.HOME||gj.tmpdir(),".bostrat"),Mm=Vv.join(Jv,"pr-links.json"),se=(e,t)=>`${e}#${Number(t)}`,Yv=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=wj.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Ur=null,Om=null,Cm=Promise.resolve();yj=new Set(["api","observed","reconciled"]);bj=new Set(["merged","closed","lost"])});import kj from"node:fs";import Pm from"node:fs/promises";import dS from"node:path";import xj from"node:os";function mS(e,t=!1){if(!t&&e-lS<vj)return;lS=e;let n=JSON.stringify({windowStart:Vs,byCaller:Object.fromEntries(Qn),lastHour:Wr});uS=uS.then(()=>Pm.mkdir(pS,{recursive:!0})).then(()=>Pm.writeFile(`${Tu}.tmp`,n)).then(()=>Pm.rename(`${Tu}.tmp`,Tu)).catch(()=>{})}function hS(e){e-Vs<fS||(Wr={start:Vs,total:Au(Qn),byCaller:Object.fromEntries(Qn)},Wr.total&&console.log(`[github] ${Wr.total} calls last hour`,Wr.byCaller),Vs=e,Qn=new Map,mS(e,!0))}function er(e="other",t=Date.now()){hS(t),Qn.set(e,(Qn.get(e)||0)+1),mS(t)}function wS(e=Date.now()){return hS(e),{windowStart:Vs,total:Au(Qn),byCaller:Object.fromEntries(Qn),lastHour:Wr,backoffUntil:Do>e?Do:null}}function qr(e){return Sj.test(String(e?.message||e||""))}function Kr(e,t=Date.now()){return Do<=t&&console.warn(`[github] rate limited \u2014 backing off ${cS/6e4} min:`,String(e?.message||e||"").slice(0,200)),Do=Math.max(Do,t+cS),Do}function Eu(e=Date.now()){return Math.max(0,Do-e)}var pS,Tu,fS,cS,vj,Vs,Qn,Wr,Do,lS,Au,uS,Sj,Ni=q(()=>{pS=process.env.BOSTRAT_STATE_DIR||dS.join(process.env.HOME||xj.tmpdir(),".bostrat"),Tu=dS.join(pS,"github-metrics.json"),fS=60*6e4,cS=5*6e4,vj=6e4,Vs=Date.now(),Qn=new Map,Wr=null,Do=0,lS=0,Au=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(kj.readFileSync(Tu,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<fS?(Vs=e.windowStart,Qn=new Map(Object.entries(e.byCaller||{})),Wr=e.lastHour||null):Au(new Map(Object.entries(e.byCaller||{})))&&(Wr={start:e.windowStart,total:Au(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}uS=Promise.resolve();Sj=/rate limit|too quickly|abuse detection|HTTP 429/i});import gS from"node:fs/promises";import yS from"node:path";import Tj from"node:os";function Iu({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};_u.push(l),_u.length>Ej&&_u.shift();let u=Dm.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)),Dm.set(e,u),_j(l),l}async function _j(e){try{await gS.mkdir(bS,{recursive:!0}),await gS.appendFile(Aj,JSON.stringify(e)+`
|
|
181
|
+
`)}catch{}}function kS(){let e={};for(let[t,n]of Dm)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:_u.slice(-20)}}var bS,Aj,Ej,_u,Dm,$u=q(()=>{bS=process.env.BOSTRAT_STATE_DIR||yS.join(process.env.HOME||Tj.tmpdir(),".bostrat"),Aj=yS.join(bS,"github-latency.ndjson"),Ej=100,_u=[],Dm=new Map});import{execFile as Ij}from"node:child_process";import{promisify as $j}from"node:util";async function Rj(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ys("git",["-C",e,"rev-parse","HEAD"]),Ys("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await C({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function qt(e,t,n={}){let{caller:r="prs",...o}=n,s=await yn({cwd:e});er(r);try{let{stdout:a}=await Ys("gh",t,{cwd:e,...Oj,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw qr(a)&&Kr(a),a}}async function Lm(){if(Ru&&Date.now()-Ru.at<6e4)return Ru.data;let e;try{er("auth");let t=await Ys("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 qa();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Ru={at:Date.now(),data:e},e}async function Cj(e){try{let{stdout:t}=await Ys("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function vS(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 jm(e){let t=(e||[]).map(vS),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 Um(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function TS(e,t){if(!t)return null;let n=await _e(e);if(!n)return null;try{let r=await qt(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 AS(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 ES(e,t){let n=await TS(e,t);return n===null?{...jm([]),unavailable:!0}:jm(AS(n))}function Ou(e,t=jm(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",state:e.state??null,mergedAt:e.mergedAt??null,closedAt:e.closedAt??null,additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function Nj(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Js.delete(s);continue}r.add(s),Js.has(s)||Js.set(s,n),o.mergeabilityPending=n-Js.get(s)<Mj}for(let o of Js.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Js.delete(o);return t}async function Xs(e,t){let n=await tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await qt(n.path,["pr","view",String(r),"--json",Fm]);return Ou(JSON.parse(o))}catch(o){if(!Um(o))throw o;let s=await qt(n.path,["pr","view",String(r),"--json",SS]),a=JSON.parse(s);return Ou(a,await ES(n.path,a.headRefOid))}}async function En(e,{caller:t="prs"}={}){try{let n=await qt(e,["pr","list","--json",Fm,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Ou(r)).sort(xS)}catch(n){if(!Um(n))throw n;let r=await qt(e,["pr","list","--json",SS,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Ou(o,await ES(e,o.headRefOid))))).sort(xS)}}function Kt(){Pi.clear()}async function jj(e,t){if(!e)return t;for(let n of t){let r=await at(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 ku(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await vu(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function _S(){let e=await Lm();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await Wn())await Cj(a.path)&&t.push(a);let n=Date.now(),r=Eu(n)>0,o=await Promise.all(t.map(async a=>{let i=Pi.get(a.path),c=i?.pending?Dj:Pj;if(i&&(r||n-i.at<c))return i.entry;try{let l=await _e(a.path).catch(()=>null),u=Nj(a.path,await En(a.path,{caller:"tab"}),n);if(await jj(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(m=>m.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&Iu({change:f.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:f.number,githubAt:f.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},p=u.some(f=>f.mergeabilityPending||f.checks.pending>0||f.autoMergeArmed);return Pi.set(a.path,{at:Date.now(),entry:d,pending:p}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:_n(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Pi.keys())s.has(a)||Pi.delete(a);return{auth:e,repos:o}}async function tr(e){let t=(await Wn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function jo(e){return ms(e)}async function Zs(e,t,{method:n="squash"}={}){let r=await tr(e);if(!Di.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 yn({cwd:r.path})).GH_TOKEN){let a=await Cp().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await qt(r.path,s)}catch(a){throw new Error(_n(a))}finally{Kt()}return{ok:!0,number:o}}async function ji(e,t){let n=await tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await qt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(_n(s))}finally{Kt()}let o=await _e(n.path).catch(()=>null);return o&&await eS(o,r).catch(()=>{}),{ok:!0,number:r}}async function Hm(e,t){let n=await tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await qt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(_n(o))}finally{Kt()}return{ok:!0,number:r}}function Wm(e){return e.green&&!e.isDraft}function Uj(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 IS(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Wm(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function $S(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Bi(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 RS(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Bi))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function OS(){let e={};for(let[t,n]of Bm)e[t]={...n};return e}async function CS(e,{method:t="merge",onMerged:n=null}={}){let r=await tr(e);if(!Di.has(t))throw new Error(`invalid merge method: ${t}`);if(Bm.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await En(r.path)).filter(Wm).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 Bm.set(r.path,s),Wj(s,o,{listPrs:()=>En(r.path),merge:a=>Zs(r.path,a,{method:t}),sleep:Fj,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function Hj(e,t){let n=Date.now()+(t.settleMaxMs??Lj),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(Bj)}}async function Wj(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 Hj(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: ${_n(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(!Wm(a)){e.skipped.push({number:a.number,title:a.title,reason:Uj(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:_n(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Kt()}}async function qm(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ys("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${_n(l)}`)}await Rj(e);let o=n||"";if((await yn({cwd:e})).GH_TOKEN){let l=await Cp().catch(()=>null);l&&(o=`${o?`${o}
|
|
182
|
+
|
|
183
|
+
`:""}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 qt(e,s)}catch(l){throw new Error(_n(l))}finally{Kt()}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 MS(e,t){let n=await tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await qt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(_n(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Um(i))throw i;let c=await qt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await TS(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(_n(i));o=AS(l).map(vS).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=qj(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await qt(n.path,["run","view",c,"--log-failed"]).then(u=>Kj(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function qj(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function Kj(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
182
184
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
183
185
|
`+o.slice(-t).join(`
|
|
184
186
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
185
|
-
`+r.slice(-n)),r}function
|
|
186
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
187
|
-
`)){let r=n.trim(),o=/^([A-Za-z-]+):\s*(.+)$/.exec(r);if(!o)continue;let s=o[1].toLowerCase(),a=o[2].trim();if(s===
|
|
188
|
-
`)){if(o.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1,hunks:[]},r=null,t.push(n);continue}if(n)if(o.startsWith("--- ")&&!r){let s=o.slice(4).trim();n.oldPath=s==="/dev/null"?null:s.replace(/^a\//,"")}else if(o.startsWith("+++ ")&&!r){let s=o.slice(4).trim();n.newPath=s==="/dev/null"?null:s.replace(/^b\//,"")}else if(o.startsWith("Binary files"))n.binary=!0;else if(o.startsWith("@@")){let s=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(o);if(!s)continue;let a=Number(s[1]),i=s[2]==null?1:Number(s[2]),c=Number(s[3]),l=s[4]==null?1:Number(s[4]);i>0&&n.deleted.push([a,i]),n.added+=l,r={oldStart:a,oldCount:i,newStart:c,newCount:l,del:[],add:[]},n.hunks.push(r)}else r&&o.startsWith("-")?r.del.push(o.slice(1)):r&&o.startsWith("+")&&r.add.push(o.slice(1))}return t}function
|
|
189
|
-
`)){let o=/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(r);if(o){n={sha:o[1],line:Number(o[3]),time:null},t.set(n.line,n);continue}n&&r.startsWith("committer-time ")&&(n.time=Number(r.slice(15))*1e3)}return t}async function
|
|
187
|
+
`+r.slice(-n)),r}function _n(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
188
|
+
`).find(n=>n.trim())||"gh failed"}var Ys,Oj,Ru,Fm,SS,Mj,Js,xS,Pj,Dj,Pi,Di,Bj,Lj,Fj,Bm,Bo=q(()=>{kr();pt();Zn();Ni();$u();de();Ys=$j(Ij),Oj={timeout:3e4,maxBuffer:8*1024*1024};Ru=null;Fm="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",SS=Fm.replace("statusCheckRollup,","");Mj=9e4,Js=new Map;xS=(e,t)=>e.number-t.number;Pj=3e4,Dj=1500,Pi=new Map;Di=new Set(["squash","merge","rebase"]);Bj=2500,Lj=9e4,Fj=e=>new Promise(t=>setTimeout(t,e));Bm=new Map});import{execFile as Gj}from"node:child_process";import{promisify as zj}from"node:util";async function Jj(e,t){try{let{stdout:n}=await Vj("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function Yj(e,t,n,{rebase:r=!1,listPrs:o=En}={}){let s=await Ii(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await $i(e.dir,n).catch(()=>0),i=t?await Jj(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(m=>m.headRefName===t)||null,u=l?"open":"none"}catch{}let d=s||a>0,p=[];s&&p.push("commit"),r&&p.push("rebase"),(s||c)&&p.push("push"),d&&(u!=="open"||s||c)&&p.push("pr");let f=p.filter(w=>!(w==="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:Cu.filter(w=>p.includes(w)),defaults:Cu.filter(w=>f.includes(w))}}async function Mu(e,{branch:t,rebase:n=!1,listPrs:r=En}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(p=>p?.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 Us(s[0].dir).catch(()=>!1))return null;let a=[];for(let p of s)a.push(await Yj(p,t,p.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(p=>p.dirty||p.aheadBase>0||p.openPr),c=i.length?i:a,l=Cu.filter(p=>c.some(f=>f.available.includes(p))),u=Cu.filter(p=>c.some(f=>f.defaults.includes(p))),d=c.filter(p=>p.openPr);return{available:l,defaults:u,prState:d.length?"open":c.every(p=>p.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(p=>({name:p.name,slug:p.slug,dirty:p.dirty,unpushed:p.unpushed,prState:p.prState,openPr:p.openPr}))}}var Vj,Cu,Km=q(()=>{uu();Bo();Vj=zj(Gj),Cu=["commit","rebase","push","pr"]});function Nu(e,t,n){let{state:r,reason:o}=Sf(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of Li)a-c.at>=NS()&&Li.delete(i);Li.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=Li.get(e);return s?Date.now()-s.at>=NS()?(Li.delete(e),!1):s.busy:!1}async function Gm(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await je(e,{lines:tt}).catch(()=>"");if(!Nu(e,t,r))return Qk(t,r)?{working:!0,prompted:!1}:n;if(Ro(t,r)){let o=await Gn(e).catch(()=>r),s=Br({name:e,agent:t},o);if(s)return!cu({name:e,agent:t},s,o)||tv(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function Lo(e,t){return(await Gm(e,t)).working}var NS,Li,Fi=q(()=>{ct();hi();fm();hm();NS=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Li=new Map});import{execFile as Xj}from"node:child_process";import{promisify as Zj}from"node:util";async function Gr(e){if(!e)return null;let t=PS.get(e);if(t&&Date.now()-t.at<eB)return t.branch;let n=null;try{let{stdout:r}=await Qj("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return PS.set(e,{at:Date.now(),branch:n}),n}function DS(e){return String(e||"").match(tB)?.[1]??null}function nB(e,t){let n=DS(e);return!!n&&!!t&&n===t}function jS(e,t){let n=DS(e);return!!n&&n!==t}function rB(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 Qs(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of rB(e))n(await Gr(r));return t}async function Pu(e,t,n=()=>!0){if(!t)return null;let r=(e??[]).filter(a=>a&&n(a)),o=r.find(a=>a.workspace?.branch===t);if(o)return o;let s=r.find(a=>nB(t,a.id));if(s)return s;for(let a of r)if(!Ve(a)&&(await Qs(a)).includes(t))return a;return null}var Qj,eB,PS,tB,Fo=q(()=>{An();Qj=Zj(Xj),eB=5e3,PS=new Map;tB=/^track\/.+-([0-9a-f]{8})$/});import{execFile as oB}from"node:child_process";import{promisify as sB}from"node:util";function ea({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function ju(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function Lu({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?dB[r]:null;return a&&s.push(a),s.push(`${HS}: ${ea({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${WS}: ${String(o).trim()}`),s}function BS(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===HS.toLowerCase()&&!t.epoch)t.epoch=ju(a);else if(s===WS.toLowerCase()&&!t.model)t.model=a;else if(s===qS.toLowerCase()&&!t.cause){let i=a.toLowerCase();Ui.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 pB(e,t=zr){return e<=t?"rework":"drift"}function fB(e,{window:t=zr,end:n=Xm}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function Fu({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return mB.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${qS} trailer`}:wB.test(s)&&!LS.test(s)?{cause:"style",level:"C",basis:"subject"}:LS.test(s)?{cause:"defect",level:"C",basis:"subject"}:hB.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 Qm({heuristic:e=null,subject:t="",ref:n=null,promptKind:r=null,fixerCategory:o=null,ticketKind:s=null}={}){let a=String(t||""),i=gB.exec(a),c=i?i[1].toLowerCase():null;return e&&(e.level==="A"||e.level==="E")?{intent:"fix",basis:e.basis}:s&&/^bug/i.test(s)?{intent:"fix",basis:`linked ticket: ${s}`}:c&&yB.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&bB.has(c)?{intent:"planned",basis:`subject prefix: ${c}:`}:s&&/^(story|feature|task)/i.test(s)?{intent:"planned",basis:`linked ticket: ${s}`}:e?{intent:"likely-fix",basis:e.basis}:r==="followup-go"?{intent:"planned",basis:"operator-tapped follow-up"}:FS.test(String(n||""))||FS.test(a)?{intent:"planned",basis:"blueprint item"}:o&&kB.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function TB(e){let t=String(e||"");return xB.some(n=>n.test(t))?"excluded":vB.some(n=>n.test(t))?"docs":SB.some(n=>n.test(t))?"config":"code"}function AB(e){let t=[],n=null,r=null;for(let o of String(e||"").split(`
|
|
190
|
+
`)){if(o.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1,hunks:[]},r=null,t.push(n);continue}if(n)if(o.startsWith("--- ")&&!r){let s=o.slice(4).trim();n.oldPath=s==="/dev/null"?null:s.replace(/^a\//,"")}else if(o.startsWith("+++ ")&&!r){let s=o.slice(4).trim();n.newPath=s==="/dev/null"?null:s.replace(/^b\//,"")}else if(o.startsWith("Binary files"))n.binary=!0;else if(o.startsWith("@@")){let s=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(o);if(!s)continue;let a=Number(s[1]),i=s[2]==null?1:Number(s[2]),c=Number(s[3]),l=s[4]==null?1:Number(s[4]);i>0&&n.deleted.push([a,i]),n.added+=l,r={oldStart:a,oldCount:i,newStart:c,newCount:l,del:[],add:[]},n.hunks.push(r)}else r&&o.startsWith("-")?r.del.push(o.slice(1)):r&&o.startsWith("+")&&r.add.push(o.slice(1))}return t}function EB(e,t){let n=Ym(e),r=Ym(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>Du&&(n=n.slice(0,Du)),r.length>Du&&(r=r.slice(0,Du));let o=new Array(r.length+1),s=new Array(r.length+1);for(let a=0;a<=r.length;a++)o[a]=a;for(let a=1;a<=n.length;a++){s[0]=a;let i=n.charCodeAt(a-1);for(let c=1;c<=r.length;c++){let l=i===r.charCodeAt(c-1)?0:1;s[c]=Math.min(o[c]+1,s[c-1]+1,o[c-1]+l)}[o,s]=[s,o]}return 1-o[r.length]/Math.max(n.length,r.length)}function _B(e,t=KS){let n=new Set,r=e?.del||[],o=e?.add||[];for(let s=0;s<r.length;s++){let a=e.oldStart+s;if(!Ym(r[s]).length){n.add(a);continue}s<o.length&&EB(r[s],o[s])>=t&&n.add(a)}return n}function IB(e){let t=[...e].sort((r,o)=>r-o),n=[];for(let r of t){let o=n[n.length-1];o&&o[0]+o[1]===r?o[1]+=1:n.push([r,1])}return n}function $B(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 US(e,t){let{stdout:n}=await Jm(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
190
192
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
191
|
-
`)}}async function SS(e,t,{fixKey:n=null,window:r=Ur,resolveOrigin:o=null,context:s={}}={}){let a=await yS(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=hS(a.message),l=n?_u(n):c.epoch,u=l?Js(l):null,{stdout:d}=await Lm(e,["diff","-U0","--no-color","-M","-C",i,t]),p=Oj(d),f=0,h=0,w=0,g=0,k=0,x=0,y=0,I=new Map;for(let O of p){if(O.binary)continue;let N=O.oldPath||O.newPath||"",z=O.deleted.reduce((K,[,be])=>K+be,0),$=Rj(N);if($==="excluded"){w+=z;continue}if($==="docs"){g+=z,k+=O.added;continue}if($==="config"){x+=z;continue}f+=O.added;let T=new Set;for(let K of O.hunks||[])for(let be of Mj(K))T.add(be);y+=T.size;let M=[];for(let[K,be]of O.deleted)for(let se=K;se<K+be;se++)T.has(se)||M.push(se);if(h+=M.length,!O.oldPath||!M.length)continue;let W;try{let K=["blame","-w","-M","-C","--line-porcelain"];for(let[be,se]of Nj(M))K.push("-L",`${be},${be+se-1}`);K.push(i,"--",O.oldPath),W=Pj((await Lm(e,K)).stdout)}catch{continue}for(let{sha:K,time:be}of W.values()){let se=I.get(K)??{lines:0,time:be};se.lines+=1,I.set(K,se)}}let R=[],P=0,H=new Map;for(let[O,{lines:N,time:z}]of I){let $=H.get(O);if(!$){try{$=await yS(e,O)}catch{$=null}H.set(O,$)}let T=$?hS($.message):{epoch:null},M=T.epoch,W=T.provider,K=T.model;if(!M&&o){let V=await o(O,$);V?.trackId&&V?.boxId!=null&&(M={trackId:V.trackId,boxId:V.boxId,epoch:V.epoch??0},W=W??V.provider??null,K=K??V.model??null)}if(!M){P+=N;continue}let be=Number.isFinite(z)?z:$?.ts??a.ts,se=Math.max(0,(a.ts-be)/mj),we=Js(M),ae=we===u?"self":yj(se,r);R.push({sha:O,key:we,trackId:M.trackId,boxId:M.boxId,epoch:M.epoch,provider:W??null,model:K??null,lines:N,ageDays:Math.round(se*100)/100,class:ae,decay:ae==="rework"?1:ae==="drift"?Math.round(bj(se,{window:r})*1e3)/1e3:0})}R.sort((O,N)=>N.lines-O.lines);let j=Ru({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=Wm({heuristic:j,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:h,excludedDeleted:w,cosmeticDeleted:y,docsDeleted:g,docsAdded:k,configDeleted:x,codeDelta:f+h+y>0,origins:R,unattributedLines:P,ghost:h===0&&f>0,heuristic:j,intent:A}}function TS(e,{minLines:t=Hm}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Xs.has(n)?Di(e)>=t:!1}function Di(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Bm(e){let t=e.genUsd;return{...e,genUsd:an(t),reworkChargedUsd:an(e.reworkChargedUsd),reworkPendingUsd:an(e.reworkPendingUsd),trueUsd:an(t+e.reworkChargedUsd),multiplier:t>0?an((t+e.reworkChargedUsd)/t):null}}function AS(e,{window:t=Ur,boxMeta:n={}}={}){let r=e||[],o=r.filter(A=>A.type==="cost.recorded"),s=r.filter(A=>A.type==="code.committed"),a=r.filter(A=>A.type==="code.rework"),i=A=>Array.isArray(A?.shas)&&A.shas.length?A.shas:A?.sha?[A.sha]:[],c=new Map,l=new Set;for(let A of r)if(A.type==="rework.cause.offered")for(let O of i(A.payload))l.add(O);else if(A.type==="rework.cause.answered"&&A.payload?.cause)for(let O of i(A.payload))c.set(O,A.payload.cause);else if(A.type==="rework.cause.retracted")for(let O of i(A.payload))c.delete(O);let u={},d=(A,O,N)=>Js({trackId:A,boxId:O,epoch:N??0}),p=(A,O={})=>{if(!u[A]){let z=_u(A)||{};u[A]={key:A,...z,provider:null,model:null,...jm()}}let N=u[A];return O.provider&&!N.provider&&(N.provider=O.provider),O.model&&!N.model&&(N.model=O.model),N};for(let A of o){if(!A.boxId)continue;let O=d(A.trackId,A.boxId,A.epoch),{usd:N,model:z}=Wt(A),$=p(O,{provider:A.provider,model:z!=="unknown"?z:null});$.genUsd+=N}let f=new Map;for(let A of s){let O=`${A.trackId}/${A.boxId}`;(f.get(O)??f.set(O,[]).get(O)).push({...A,ts:A.payload?.ts??A.ts})}for(let A of f.values())A.sort((O,N)=>O.ts-N.ts);let h=(A,O,N,z)=>{let $=f.get(`${A}/${O}`)||[],T=-1/0;for(let K of $)K.ts<z&&(T=K.ts);let M=0,W=0;for(let K of o)K.trackId!==A||K.boxId!==O||(K.epoch??0)===(N??0)&&(K.ts<=T||K.ts>z||(M+=Wt(K).usd,W++));return{usd:M,turns:W,from:Number.isFinite(T)?T:null}},w={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},g={A:0,B:0,C:0,E:0,none:0},k={fix:0,"likely-fix":0,planned:0,unknown:0},x=0,y=[],I=[],R={},P={},H=new Set;for(let A of a){let O=A.payload||{};if(!O.sha||H.has(O.sha))continue;if(H.add(O.sha),O.codeDelta===!1){x++;continue}let N=_u(O.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:z,turns:$}=h(N.trackId,N.boxId,N.epoch,O.ts??A.ts),T=wj.includes(O.intent?.intent)?O.intent.intent:"unknown";k[T]+=1;let M=null;c.has(O.sha)?M={cause:c.get(O.sha),level:"B",basis:"operator"}:(O.heuristic?.level&&O.heuristic.level!=="C"||O.heuristic)&&(M=O.heuristic);let W=M?.level??"none";g[W]=(g[W]||0)+1;let K=hj.has(W),be=Number(O.deleted)||0,se=Array.isArray(O.origins)?O.origins:[],we=se.filter(V=>V.class==="rework").reduce((V,de)=>V+de.lines,0),ae={sha:O.sha,subject:O.subject??"",ts:O.ts??A.ts,trackId:A.trackId,fixKey:O.fixKey??null,fixProvider:O.fixProvider??A.provider??null,fixUsd:an(z),fixTurns:$,linesReworked:we,unattributedLines:Number(O.unattributedLines)||0,ghost:O.ghost===!0,intent:{intent:T,basis:O.intent?.basis??null},verdict:M?{cause:M.cause,level:W,basis:M.basis??null}:null,charged:an(0),origins:[]};if(T==="planned"||M?.cause===Iu){w.planned+=z;for(let V of se)V.class==="rework"&&ae.origins.push({key:V.key,provider:V.provider??null,model:V.model??null,lines:V.lines,ageDays:V.ageDays,class:V.class,usd:0})}else if(O.ghost||be===0)w.ghost+=z;else{let V=0;for(let re of se){let Y=re.lines/be,$e=z*Y;if(V+=$e,re.class==="self"){w.self+=$e;continue}let Pe=re.class==="rework"?1:Number(re.decay)||0,me=$e*Pe;if(w.decayed+=$e-me,me<=0)continue;let _=p(re.key,{provider:re.provider,model:re.model});_.fixes+=1,_.linesReworked+=re.class==="rework"?re.lines:0;let F=M?.cause&&Pi.includes(M.cause)?M.cause:"unclassified";_.causes[F]+=1,K?(_.reworkChargedUsd+=me,w.charged+=me,ae.charged=an(ae.charged+me)):(_.reworkPendingUsd+=me,w.pending+=me),ae.origins.push({key:re.key,provider:_.provider,model:_.model,lines:re.lines,ageDays:re.ageDays,class:re.class,usd:an(me)})}let de=Math.max(0,z-V);w.unattributed+=de}y.push(ae),!c.has(O.sha)&&l.has(O.sha)&&I.push(O.sha)}for(let A of Object.values(u)){let O=A.provider||"unknown",N=A.model||"unknown",z=$=>{$.genUsd+=A.genUsd,$.reworkChargedUsd+=A.reworkChargedUsd,$.reworkPendingUsd+=A.reworkPendingUsd,$.linesReworked+=A.linesReworked,$.fixes+=A.fixes;for(let T of Object.keys($.causes))$.causes[T]+=A.causes[T]};z(R[O]||=jm()),z(P[N]||=jm())}y.sort((A,O)=>O.ts-A.ts);let j=Object.values(u).map(A=>{let O=n[`${A.trackId}/${A.boxId}`]||{};return Bm({...A,trackName:O.trackName??null,label:O.label??null})}).sort((A,O)=>O.reworkChargedUsd+O.reworkPendingUsd-(A.reworkChargedUsd+A.reworkPendingUsd)||O.genUsd-A.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${vS} similar are counted, not blamed); fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause; planned work (follow-up, blueprint item, feat:/docs: prefix, non-fix prompt) is generation cost`,intents:k,noCodeDelta:x,byEpoch:Object.fromEntries(j.map(A=>[A.key,A])),epochs:j,byProvider:Object.fromEntries(Object.entries(R).map(([A,O])=>[A,Bm(O)])),byModel:Object.fromEntries(Object.entries(P).map(([A,O])=>[A,Bm(O)])),buckets:Object.fromEntries(Object.entries(w).map(([A,O])=>[A,an(O)])),fixUsdTotal:an(Object.values(w).reduce((A,O)=>A+O,0)),fixes:y,verdicts:g,pendingTaps:I,commits:s.length}}var pj,fj,Lm,bS,kS,xS,mj,Ur,Um,Hm,Pi,Iu,hj,wj,Xs,vS,gj,kj,wS,xj,vj,Sj,Tj,Aj,gS,Ej,_j,Ij,$j,Fm,Eu,an,jm,Ou=q(()=>{Rr();pj=dj(uj),fj=64*1024*1024,Lm=(e,t)=>pj("git",["-C",e,...t],{maxBuffer:fj,timeout:6e4}),bS="Bostrat-Epoch",kS="Bostrat-Model",xS="Bostrat-Fix-Cause",mj=24*36e5,Ur=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Um=90,Hm=3,Pi=Object.freeze(["defect","design","requirements","style"]),Iu="not-a-fix",hj=new Set(["A","B","E"]),wj=Object.freeze(["fix","likely-fix","planned","unknown"]),Xs=new Set(["fix","likely-fix"]),vS=.8;gj={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};kj=/^revert\b|^revert "/i,wS=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,xj=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,vj=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;Sj=/^([a-z]+)(\([^)]*\))?!?:\s/i,Tj=new Set(["fix","hotfix","bugfix","revert"]),Aj=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),gS=/\bBP-[A-Z]*\d+\b/i,Ej=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);_j=[/(^|\/)\.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)\//],Ij=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],$j=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Fm=e=>String(e??"").replace(/\s+/g," ").trim(),Eu=400;an=e=>Math.round(e*1e4)/1e4,jm=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as Dj}from"node:child_process";import{promisify as jj}from"node:util";function ES(e,t=Date.now()){return Ep(e,t)??0}function Lj(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 _S(e,t,{now:n=Date.now()}={}){let r=ES(t,n),o=await G(e);return $u({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:Lj(o,t.name,r)})}function Fj(e,t){let n=null;for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="turn.user"||o.boxId!==t||o.actor!=="user")continue;let s=o.payload||{};if(s.note?.kind!=="task"){if(o.source==="orchestrator"&&typeof s.text=="string"&&s.text.trim())return{text:s.text,kind:s.kind??null};!n&&typeof s.promptHead=="string"&&s.promptHead.trim()&&(n={text:s.promptHead,kind:s.kind??null})}}return n}function Uj(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=Fj(n,t.name),c=i?Wn(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function Hj(e,t){try{let{stdout:n}=await qm(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 Wj(e){try{let{stdout:t}=await qm(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function IS(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 G(e);if(a.some(h=>h.type==="code.committed"&&h.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:h}=await qm(r,["show","-s","--format=%ct",n]),w=Number(h.trim())*1e3;Number.isFinite(w)&&w>0&&(i=w)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=ES(t,i),l=await Hj(r,n),u=await Wj(r),d=await C(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:u,...l,ts:i}}),p=null,f=!1;try{let h=await SS(r,n,{fixKey:Js({trackId:e.id,boxId:t.name,epoch:c}),window:Ur,context:{...Uj(e,t,a,c),ref:u,provider:t.agent??null}});if(h&&(p=await C(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:h}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&TS(h))){let g=Di(h),k=h.origins.filter(x=>x.class==="rework").slice(0,3);await C(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:{sha:n,subject:h.subject,linesReworked:g,origins:k.map(x=>({key:x.key,provider:x.provider,model:x.model,lines:x.lines,ageDays:x.ageDays})),heuristic:h.heuristic,intent:h.intent,ts:i}}),f=!0}}catch(h){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${h?.message||h}`)}return{recorded:!0,event:d,rework:p,offered:f}}function $S(e,t){let n=0;for(let r of e)r.payload?.sha===t&&(r.type==="rework.cause.answered"?n++:r.type==="rework.cause.retracted"&&n--);return n>0}async function RS(e,t,n){if(!Pi.includes(n)&&n!==Iu)throw new Error(`bad cause "${n}" (${Pi.join("|")}|${Iu})`);let r=await G(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if($S(r,t))throw new Error("already answered");return C(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,...Array.isArray(o.payload?.shas)?{shas:o.payload.shas}:{},cause:n,level:"B",ts:Date.now()}})}async function OS(e,t){let n=await G(e);if(!$S(n,t))throw new Error("no standing answer to retract");let r=[...n].reverse().find(o=>o.type==="rework.cause.answered"&&o.payload?.sha===t);return C(e,{type:"rework.cause.retracted",actor:"user",source:"orchestrator",boxId:r?.boxId??null,causationId:r?.id,payload:{sha:t,...Array.isArray(r?.payload?.shas)?{shas:r.payload.shas}:{},ts:Date.now()}})}async function Cu(e,{repo:t=null,number:n,title:r="",branch:o=null,url:s=null}={},{log:a=console,now:i=Date.now()}={}){try{if(!e?.id||n==null)return{offered:!1,reason:"incomplete"};let c=await G(e);if(c.some(x=>x.type==="rework.cause.offered"&&x.payload?.pr?.number===n&&(t==null||x.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let x of c)if(x.type==="rework.cause.offered")for(let y of Array.isArray(x.payload?.shas)?x.payload.shas:[x.payload?.sha])y&&l.add(y);let u=new Map;for(let x of c)x.type==="code.committed"&&x.payload?.sha&&u.set(x.payload.sha,x.payload.ref??null);let d=Wm({heuristic:Ru({subject:r}),subject:r,ref:o}),p=[];for(let x of c){if(x.type!=="code.rework")continue;let y=x.payload||{};if(!y.sha||l.has(y.sha)||y.codeDelta===!1||o&&u.has(y.sha)&&u.get(y.sha)!==o)continue;let I=y.intent?.intent??"unknown";(Xs.has(I)||I==="unknown"&&Xs.has(d.intent))&&Di(y)!==0&&p.push(y)}let f=p.reduce((x,y)=>x+Di(y),0);if(f<Hm)return{offered:!1,reason:"below-threshold"};let h=new Map;for(let x of p)for(let y of x.origins??[]){if(y.class!=="rework")continue;let I=h.get(y.key)??{key:y.key,provider:y.provider??null,model:y.model??null,lines:0,ageDays:y.ageDays};I.lines+=y.lines,I.ageDays=Math.min(I.ageDays,y.ageDays),h.set(y.key,I)}let w=p.map(x=>x.sha),g=p.map(x=>x.heuristic).find(x=>x&&x.level!=="C")??Ru({subject:r})??p.map(x=>x.heuristic).find(Boolean)??null,k=Xs.has(d.intent)?d:p.find(x=>Xs.has(x.intent?.intent))?.intent??d;return await C(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:null,payload:{sha:w[w.length-1],shas:w,pr:{repo:t,number:n,title:String(r||"").slice(0,200),url:s,branch:o},subject:String(r||"").slice(0,200)||p[p.length-1].subject,linesReworked:f,origins:[...h.values()].sort((x,y)=>y.lines-x.lines).slice(0,3),heuristic:g,intent:k,ts:i}}),{offered:!0,shas:w}}catch(c){return a.warn?.(`[rework] ${e?.id}: PR-level offer for #${n} failed: ${c?.message||c}`),{offered:!1,reason:"error"}}}var Bj,qm,Mu=q(()=>{fe();jl();Ou();Bj=jj(Dj),qm=(e,t)=>Bj("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as qj}from"node:child_process";import{promisify as Gj}from"node:util";async function Gm(e,t,{midTurn:n=Mo,pollMs:r=Kj,capMs:o=Vj}={}){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 jS(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){CS.lastIndex=n;let o=CS.exec(t);if(!o)break;n=o.index+o[0].length;let s=o[1]==="-",a=o[2]??o[3]??o[4],i=t.indexOf(`
|
|
192
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+eB(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 Km(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function LS(e){let t=jS(e),n=t.match(Xj);if(!n)return null;let r=Km(t,n.index);return Qj.test(r)?null:{repo:r.match(BS)?.[1]??null,head:r.match(tB)?.[1]??null}}function FS(e){let t=jS(e),n=t.match(Zj);if(!n)return null;let r=Km(t,n.index),o=Km(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("-")){rB.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(BS)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function sB(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Jn("observer");let{stdout:c}=await MS("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",oB],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Br(l)&&Lr(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-zj&&!n(c)),s=await wn({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 aB(e,t){if(t)return[t];let n=[];for(let r of US(e)){let o=await _e(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Ln().catch(()=>[])){let o=await _e(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function US(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 iB(e,t,n){let r=await Ys(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)||nB(c.headRefName,e.id),i=[];for(let c of await aB(e,t?.repo??null)){let l;try{l=await sB(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await st(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function HS(){for(let e of[...Vm])e()}function WS(e,t,{delays:n=NS,turnEnd:r}={}){let o=LS(t.command);if(!o)return null;let s=!!FS(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Hr.has(a))return null;Hr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>{let g=()=>{clearTimeout(k),Vm.delete(g),w()},k=setTimeout(g,l);Vm.add(g)});let u=await iB(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:h}=await Ct({repo:d,number:p.number,trackId:e.id,url:p.url,headRef:p.headRefName,title:p.title,source:"observed"});return!h&&!(f?.trackId===e.id&&await Kn(d,p.number))?null:(await Gm(t.box,t.agent,r),await C({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:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{Hr.delete(a)}})();return c.catch(()=>Hr.delete(a)),c}function qS(e,t,{delays:n=NS,turnEnd:r,openedWait:o}={}){let s=FS(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Hr.has(a))return null;Hr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let d=[];if(c)d=[c];else{let h=US(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let g=await _e(w).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await Ys(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(h,w)=>{let g=await st(c,h.number);return g&&g.trackId!==e.id?null:(g||await Ct({repo:c,number:h.number,trackId:e.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await Ei(c,h.number),w&&((await G(e).catch(()=>[])).some(y=>y.type==="pr.state.changed"&&(y.payload?.change==="auto-armed"||y.payload?.change==="merged")&&y.payload?.number===h.number)||(await Gm(t.box,t.agent,r),await C({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},f=null;for(let h of n){await new Promise(P=>setTimeout(P,h));let w=[];for(let P of d)try{Jn("observer");let{stdout:H}=await MS("gh",["pr","view",String(l),"--repo",P,"--json",cB],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await wn({slug:P})}}),j=JSON.parse(H||"null");j?.number&&w.push({slug:P,pr:j})}catch(H){Br(H)&&Lr(H)}let g=w.find(P=>P.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!g)continue;c=g.slug;let k=g.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(f=k,s.auto)return p(k,!0);continue}let x=await st(c,k.number);if(x&&x.trackId!==e.id)return null;x||await Ct({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Gm(t.box,t.agent,r);let y=await G(e).catch(()=>[]);if(y.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="merged"&&P.payload?.number===k.number))return null;let I=P=>P.some(H=>H.type==="pr.state.changed"&&H.payload?.change==="opened"&&H.payload?.number===k.number),R=0;if(!I(y)){if(LS(t.command)&&await Kn(c,k.number)){let P=Date.now();R=P+1,await C({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:P,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await st(c,k.number))?.announced){let P=o?.capMs??Yj,H=o?.pollMs??Jj,j=Date.now()+P;for(;Date.now()<j&&(await new Promise(A=>setTimeout(A,H)),!I(await G(e).catch(()=>[]))););}}return await C({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(),R),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Cu(e,{repo:c,number:k.number,title:k.title,branch:k.headRefName,url:k.url}).catch(()=>{}),await Qn(c,k.number).catch(()=>{}),await Zn(c,k.number).catch(()=>{}),await Dr(c,k.number,"merged"),k}return f?p(f,!1):null}finally{Hr.delete(a)}})();return i.catch(()=>Hr.delete(a)),i}var MS,NS,Kj,Vj,zj,Yj,Jj,PS,DS,Xj,Zj,Qj,CS,eB,BS,tB,nB,rB,oB,Hr,Vm,cB,zm=q(()=>{fe();hr();ut();Ii();zn();er();Ni();No();Mu();MS=Gj(qj),NS=[4e3,15e3,45e3],Kj=2500,Vj=5*6e4;zj=15e3,Yj=9e4,Jj=2e3,PS=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,DS=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,Xj=new RegExp(`${PS}${DS}create\\b`),Zj=new RegExp(`${PS}${DS}merge\\b`),Qj=/\s--web\b/,CS=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,eB=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");BS=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,tB=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,nB=mS;rB=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);oB="number,url,title,headRefName,createdAt";Hr=new Set,Vm=new Set;cB="number,url,title,headRefName,state"});import lB from"node:fs/promises";import Jm from"node:path";import uB from"node:os";function Ym(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Nu(e){let t=dB.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Ym(n,"from"),s=o?o.replace(/^uds:/,""):null,a=GS(s);return{socket:s,pid:a,name:Ym(n,"from-name"),mode:Ym(n,"from-mode"),text:r.trim()}}function GS(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function pB(){return process.env.CLAUDE_CONFIG_DIR?Jm.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Jm.join(uB.homedir(),".claude","sessions")}async function fB(e){let t=GS(e);if(!t)return null;try{let n=await lB.readFile(Jm.join(pB(),`${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 KS(e,t){let n=await fB(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 dB,Xm=q(()=>{dB=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function Zm(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Qm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Zm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),VS);try{let a=await zS(`${Re(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function YS(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(!Zm(s))return{ok:!1,source:"unconfigured"};let i=Pu.get(e.id)||0;if(a()-i<mB)return{ok:!1,source:"debounced"};Pu.set(e.id,a());let c=await ji({env:s}),l=c.loops.find(p=>p.trackId===e.id);if(!l)return Pu.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 Qm({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||Pu.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function ji({env:e=process.env}={}){if(!Zm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),VS);try{let r=await zS(`${Re(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var VS,zS,mB,Pu,eh=q(()=>{ut();VS=1e4,zS=(...e)=>fetch(...e);mB=10*6e4,Pu=new Map});function JS(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function XS(e,t,n=th){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await wB(`${Re(t)}${e}`,{headers:gB(t),signal:r.signal})}finally{clearTimeout(o)}}async function Bi({env:e=process.env,force:t=!1,timeoutMs:n=th}={}){if(!JS(e))return{followups:[],source:"unconfigured"};if(!t&&Po&&Date.now()-Po.at<hB)return{followups:Po.followups,source:"cache"};try{let r=await XS("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return Po={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Po?{followups:Po.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function ZS(e,{env:t=process.env,timeoutMs:n=th}={}){if(!e)return{followup:null,source:"unconfigured"};if(!JS(t))return{followup:null,source:"unconfigured"};try{let r=await XS(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=Po?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var hB,th,Po,wB,gB,QS=q(()=>{ut();hB=6e4,th=5e3,Po=null,wB=(...e)=>fetch(...e);gB=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function Wr(e,t){if(nh.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}nh.add(e);try{return await t()}finally{nh.delete(e)}}var nh,Li=q(()=>{nh=new Set});import{randomUUID as Qs,createHash as yB}from"node:crypto";function rT(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function SB(e){for(let[t,n]of vB)if(t.test(String(e||"")))return`a standing prompt must not embed this Track's specifics (${n}) \u2014 the card supplies them at fire time; arm a one-shot with \`offer\` instead`;return null}function oT(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Zs(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>eT)throw new Error(`label must be \u2264${eT} characters`);let o=Zs(n.trigger)||(n.ship?"now":""),s=t?kB:nT.filter(u=>u!=="track.opened");if(!s.includes(o))throw new Error(`trigger must be one of ${s.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let a=n.ship&&o==="now"?AB(n.ship):null,i=Zs(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>tT)throw new Error(`prompt must be \u2264${tT} bytes`);if(t){let u=SB(i);if(u)throw new Error(u)}let c=Zs(n.why).slice(0,bB)||null,l=t?TB(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function TB(e){if(e==null||e===""||e==="*")return null;let t=String(e).trim();if(!/^[\w.-]+\/[\w.-]+$/.test(t))throw new Error(`repo must be a forge slug like owner/name, got "${e}"`);return t}function AB(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):rh.slice();for(let i of n)if(!rh.includes(i))throw new Error(`ship.actions may only contain ${rh.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=Zs(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>Zs(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Du(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}async function sT(e,t,{pull:n=Bi,ship:r={}}={}){if(ah(e))throw new Error("follow-ups don't fire on a loop's run Track \u2014 report the outcome with `bostrat-loop report` instead");let o=oT(t);if(o.ship)return{kind:"ship",...await $B(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null};if(o.trigger==="now"){let g=Qs(),k=Qs();return await C(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:g,trigger:"now",label:o.label,prompt:o.prompt,why:o.why}}),await C(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:k,trigger:"now",repo:e.workspace?.repo||null,context:{},rows:[{armId:g,label:o.label,prompt:o.prompt,promptHash:oh(o.prompt),origin:"agent",why:o.why}]}}),{kind:"now",armId:g,offerId:k,...o}}let c=e.workspace?.repo||null,{followups:l}=await n().catch(()=>({followups:[]})),u=rT(o.label),d=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(d)return{kind:"covered",covered:{id:d.id,label:d.label},...o};let p=await G(s),f=Du(p,{trigger:o.trigger}),h=f.length?f[f.length-1].armId:null,w=Qs();return await C(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:w,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...h?{replaces:h}:{}}}),{kind:"armed",armId:w,replaces:h,...o}}async function aT(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await G(n);if(!Du(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await C(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function iT(e,t,{pull:n=Bi}={}){if(ah(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=oT(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await G(s)).reduce((g,k)=>k.type==="followup.create.offered"?k.payload?.proposalId??g:k.type==="followup.create.answered"&&k.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=rT(r.label),d=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),p=d.find(g=>g.status==="deleted"),f=d.find(g=>g.status!=="deleted");if(f)throw new Error(`"${f.label}" (${sh[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let g=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${g} \u2014 do not re-propose it`)}let h=Qs(),w=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await C(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:w?.agent??null,payload:{ts:Date.now(),proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function cT(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await G(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await C(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await C(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await C(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Fi(e,t,n={},{pull:r=Bi}={}){if(!e||ah(e))return null;let o={id:e.id,cwd:e.cwd},s=await G(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null;if(t==="pr.merged"){let p=w=>{let g=(w?.prs||[]).filter(k=>Number.isFinite(Number(k?.number))).map(k=>`${k?.repo??w?.repo??""}#${Number(k.number)}`);return Number.isFinite(Number(w?.prNumber))&&g.push(`${w?.repo??""}#${Number(w.prNumber)}`),g},f=new Set(p(n));if(s.some(w=>w.type==="followup.offered"&&w.payload?.trigger==="pr.merged"&&p(w.payload?.context).some(g=>f.has(g))))return null}let i=Du(s,{trigger:t}).map(p=>({armId:p.armId,label:p.label,prompt:p.prompt,promptHash:oh(p.prompt),origin:"agent",why:p.why??null})),c=[];if(t!=="now"){let{followups:p}=await r().catch(()=>({followups:[]}));c=p.filter(f=>f.status==="active"&&f.mode!=="off"&&f.trigger===t&&(f.repo==null||f.repo===a)).sort((f,h)=>{let w=g=>g.stats?.offered?(g.stats.accepted||0)/g.stats.offered:0;return w(h)-w(f)||(f.origin==="operator"?-1:0)-(h.origin==="operator"?-1:0)||(h.createdAt||0)-(f.createdAt||0)}).map(f=>({followupId:f.id,label:f.label,promptPreview:EB(f.prompt),promptHash:oh(f.prompt),origin:f.origin,repo:f.repo??null}))}let l=[...i,...c].slice(0,xB);if(!l.length)return null;let u=Qs(),d={ts:Date.now(),offerId:u,trigger:t,repo:a,context:{...n,repo:a},rows:l};return await C(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:d}),d}function _B(e){let t=e?.context||{},n=Array.isArray(t.prs)&&t.prs.length?t.prs:t.prNumber?[{number:t.prNumber,title:t.prTitle,url:t.prUrl,repo:t.repo}]:[],r=o=>`PR #${o.number}${o.title?` "${o.title}"`:""}${o.url?` (${o.url})`:""}${o.repo?` on ${o.repo}`:""}`;switch(e?.trigger){case"pr.merged":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} merged:`:"Follow-up the operator tapped after this Track's PR merged:";case"pr.opened":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} opened:`:"Follow-up the operator tapped after this Track's PR opened:";case"checks.green":return n.length?`Follow-up the operator tapped after CI went green on ${n.map(r).join(" and ")}:`:"Follow-up the operator tapped after CI went green:";case"ship.done":return"Follow-up the operator tapped after the ship you just ran completed:";default:return"Follow-up the operator tapped:"}}async function lT(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){if(!t)throw new Error("offerId required");return Wr(`fu:${e.id}:${t}`,()=>IB(e,{offerId:t,followupId:n,armId:r,promoted:o},s))}async function IB(e,{offerId:t,followupId:n,armId:r,promoted:o},s){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=ZS}=s,l={id:e.id,cwd:e.cwd},u=await G(l),d=u.find(y=>y.type==="followup.offered"&&y.payload?.offerId===t);if(!d)throw new Error("unknown offer");if(u.some(y=>y.type==="followup.answered"&&y.payload?.offerId===t))throw new Error("offer already answered");let p=d.payload.rows||[],f=y=>C(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...y}});if(!n&&!r)return await f({action:"dismiss",followupIds:p.map(y=>y.followupId).filter(Boolean),armIds:p.map(y=>y.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let h=null,w=null;if(n){if(!p.some(R=>R.followupId===n))throw new Error("that follow-up is not on this card");let{followup:y,source:I}=await c(n);if(I==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!y||y.status!=="active"){await f({action:"removed",followupId:n});let R=new Error("removed in Settings since it was offered");throw R.code="removed",R}h=y.prompt,w=y.label}else{let y=p.find(R=>R.armId===r);if(!y)throw new Error("that arm is not on this card");if(h=u.find(R=>R.type==="followup.armed"&&R.payload?.armId===r)?.payload?.prompt??y.prompt,w=y.label,!h)throw new Error("the arm's prompt is missing from the log")}let g=!1;if(e.archivedAt){if(!i)throw new Error("this Track is archived");await i(e.id,{freshSession:!0}),g=!0}if(!a)throw new Error("sayToLead unavailable");let k=`${_B(d.payload)}
|
|
193
|
+
`)}}async function GS(e,t,{fixKey:n=null,window:r=zr,resolveOrigin:o=null,context:s={}}={}){let a=await US(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=BS(a.message),l=n?ju(n):c.epoch,u=l?ea(l):null,{stdout:d}=await Jm(e,["diff","-U0","--no-color","-M","-C",i,t]),p=AB(d),f=0,w=0,m=0,g=0,b=0,x=0,y=0,$=new Map;for(let R of p){if(R.binary)continue;let G=R.oldPath||R.newPath||"",V=R.deleted.reduce((z,[,ue])=>z+ue,0),A=TB(G);if(A==="excluded"){m+=V;continue}if(A==="docs"){g+=V,b+=R.added;continue}if(A==="config"){x+=V;continue}f+=R.added;let _=new Set;for(let z of R.hunks||[])for(let ue of _B(z))_.add(ue);y+=_.size;let N=[];for(let[z,ue]of R.deleted)for(let pe=z;pe<z+ue;pe++)_.has(pe)||N.push(pe);if(w+=N.length,!R.oldPath||!N.length)continue;let U;try{let z=["blame","-w","-M","-C","--line-porcelain"];for(let[ue,pe]of IB(N))z.push("-L",`${ue},${ue+pe-1}`);z.push(i,"--",R.oldPath),U=$B((await Jm(e,z)).stdout)}catch{continue}for(let{sha:z,time:ue}of U.values()){let pe=$.get(z)??{lines:0,time:ue};pe.lines+=1,$.set(z,pe)}}let O=[],M=0,H=new Map;for(let[R,{lines:G,time:V}]of $){let A=H.get(R);if(!A){try{A=await US(e,R)}catch{A=null}H.set(R,A)}let _=A?BS(A.message):{epoch:null},N=_.epoch,U=_.provider,z=_.model;if(!N&&o){let W=await o(R,A);W?.trackId&&W?.boxId!=null&&(N={trackId:W.trackId,boxId:W.boxId,epoch:W.epoch??0},U=U??W.provider??null,z=z??W.model??null)}if(!N){M+=G;continue}let ue=Number.isFinite(V)?V:A?.ts??a.ts,pe=Math.max(0,(a.ts-ue)/cB),Se=ea(N),Ae=Se===u?"self":pB(pe,r);O.push({sha:R,key:Se,trackId:N.trackId,boxId:N.boxId,epoch:N.epoch,provider:U??null,model:z??null,lines:G,ageDays:Math.round(pe*100)/100,class:Ae,decay:Ae==="rework"?1:Ae==="drift"?Math.round(fB(pe,{window:r})*1e3)/1e3:0})}O.sort((R,G)=>G.lines-R.lines);let P=Fu({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),E=Qm({heuristic:P,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:w,excludedDeleted:m,cosmeticDeleted:y,docsDeleted:g,docsAdded:b,configDeleted:x,codeDelta:f+w+y>0,origins:O,unattributedLines:M,ghost:w===0&&f>0,heuristic:P,intent:E}}function zS(e,{minLines:t=Zm}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return ta.has(n)?Hi(e)>=t:!1}function Hi(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Vm(e){let t=e.genUsd;return{...e,genUsd:sn(t),reworkChargedUsd:sn(e.reworkChargedUsd),reworkPendingUsd:sn(e.reworkPendingUsd),trueUsd:sn(t+e.reworkChargedUsd),multiplier:t>0?sn((t+e.reworkChargedUsd)/t):null}}function VS(e,{window:t=zr,boxMeta:n={}}={}){let r=e||[],o=r.filter(E=>E.type==="cost.recorded"),s=r.filter(E=>E.type==="code.committed"),a=r.filter(E=>E.type==="code.rework"),i=E=>Array.isArray(E?.shas)&&E.shas.length?E.shas:E?.sha?[E.sha]:[],c=new Map,l=new Set;for(let E of r)if(E.type==="rework.cause.offered")for(let R of i(E.payload))l.add(R);else if(E.type==="rework.cause.answered"&&E.payload?.cause)for(let R of i(E.payload))c.set(R,E.payload.cause);else if(E.type==="rework.cause.retracted")for(let R of i(E.payload))c.delete(R);let u={},d=(E,R,G)=>ea({trackId:E,boxId:R,epoch:G??0}),p=(E,R={})=>{if(!u[E]){let V=ju(E)||{};u[E]={key:E,...V,provider:null,model:null,...zm()}}let G=u[E];return R.provider&&!G.provider&&(G.provider=R.provider),R.model&&!G.model&&(G.model=R.model),G};for(let E of o){if(!E.boxId)continue;let R=d(E.trackId,E.boxId,E.epoch),{usd:G,model:V}=Wt(E),A=p(R,{provider:E.provider,model:V!=="unknown"?V:null});A.genUsd+=G}let f=new Map;for(let E of s){let R=`${E.trackId}/${E.boxId}`;(f.get(R)??f.set(R,[]).get(R)).push({...E,ts:E.payload?.ts??E.ts})}for(let E of f.values())E.sort((R,G)=>R.ts-G.ts);let w=(E,R,G,V)=>{let A=f.get(`${E}/${R}`)||[],_=-1/0;for(let z of A)z.ts<V&&(_=z.ts);let N=0,U=0;for(let z of o)z.trackId!==E||z.boxId!==R||(z.epoch??0)===(G??0)&&(z.ts<=_||z.ts>V||(N+=Wt(z).usd,U++));return{usd:N,turns:U,from:Number.isFinite(_)?_:null}},m={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},g={A:0,B:0,C:0,E:0,none:0},b={fix:0,"likely-fix":0,planned:0,unknown:0},x=0,y=[],$=[],O={},M={},H=new Set;for(let E of a){let R=E.payload||{};if(!R.sha||H.has(R.sha))continue;if(H.add(R.sha),R.codeDelta===!1){x++;continue}let G=ju(R.fixKey)||{trackId:E.trackId,boxId:E.boxId,epoch:E.epoch??0},{usd:V,turns:A}=w(G.trackId,G.boxId,G.epoch,R.ts??E.ts),_=uB.includes(R.intent?.intent)?R.intent.intent:"unknown";b[_]+=1;let N=null;c.has(R.sha)?N={cause:c.get(R.sha),level:"B",basis:"operator"}:(R.heuristic?.level&&R.heuristic.level!=="C"||R.heuristic)&&(N=R.heuristic);let U=N?.level??"none";g[U]=(g[U]||0)+1;let z=lB.has(U),ue=Number(R.deleted)||0,pe=Array.isArray(R.origins)?R.origins:[],Se=pe.filter(W=>W.class==="rework").reduce((W,te)=>W+te.lines,0),Ae={sha:R.sha,subject:R.subject??"",ts:R.ts??E.ts,trackId:E.trackId,fixKey:R.fixKey??null,fixProvider:R.fixProvider??E.provider??null,fixUsd:sn(V),fixTurns:A,linesReworked:Se,unattributedLines:Number(R.unattributedLines)||0,ghost:R.ghost===!0,intent:{intent:_,basis:R.intent?.basis??null},verdict:N?{cause:N.cause,level:U,basis:N.basis??null}:null,charged:sn(0),origins:[]};if(_==="planned"||N?.cause===Bu){m.planned+=V;for(let W of pe)W.class==="rework"&&Ae.origins.push({key:W.key,provider:W.provider??null,model:W.model??null,lines:W.lines,ageDays:W.ageDays,class:W.class,usd:0})}else if(R.ghost||ue===0)m.ghost+=V;else{let W=0;for(let ie of pe){let J=ie.lines/ue,ge=V*J;if(W+=ge,ie.class==="self"){m.self+=ge;continue}let Oe=ie.class==="rework"?1:Number(ie.decay)||0,Me=ge*Oe;if(m.decayed+=ge-Me,Me<=0)continue;let I=p(ie.key,{provider:ie.provider,model:ie.model});I.fixes+=1,I.linesReworked+=ie.class==="rework"?ie.lines:0;let j=N?.cause&&Ui.includes(N.cause)?N.cause:"unclassified";I.causes[j]+=1,z?(I.reworkChargedUsd+=Me,m.charged+=Me,Ae.charged=sn(Ae.charged+Me)):(I.reworkPendingUsd+=Me,m.pending+=Me),Ae.origins.push({key:ie.key,provider:I.provider,model:I.model,lines:ie.lines,ageDays:ie.ageDays,class:ie.class,usd:sn(Me)})}let te=Math.max(0,V-W);m.unattributed+=te}y.push(Ae),!c.has(R.sha)&&l.has(R.sha)&&$.push(R.sha)}for(let E of Object.values(u)){let R=E.provider||"unknown",G=E.model||"unknown",V=A=>{A.genUsd+=E.genUsd,A.reworkChargedUsd+=E.reworkChargedUsd,A.reworkPendingUsd+=E.reworkPendingUsd,A.linesReworked+=E.linesReworked,A.fixes+=E.fixes;for(let _ of Object.keys(A.causes))A.causes[_]+=E.causes[_]};V(O[R]||=zm()),V(M[G]||=zm())}y.sort((E,R)=>R.ts-E.ts);let P=Object.values(u).map(E=>{let R=n[`${E.trackId}/${E.boxId}`]||{};return Vm({...E,trackName:R.trackName??null,label:R.label??null})}).sort((E,R)=>R.reworkChargedUsd+R.reworkPendingUsd-(E.reworkChargedUsd+E.reworkPendingUsd)||R.genUsd-E.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${KS} similar are counted, not blamed); fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause; planned work (follow-up, blueprint item, feat:/docs: prefix, non-fix prompt) is generation cost`,intents:b,noCodeDelta:x,byEpoch:Object.fromEntries(P.map(E=>[E.key,E])),epochs:P,byProvider:Object.fromEntries(Object.entries(O).map(([E,R])=>[E,Vm(R)])),byModel:Object.fromEntries(Object.entries(M).map(([E,R])=>[E,Vm(R)])),buckets:Object.fromEntries(Object.entries(m).map(([E,R])=>[E,sn(R)])),fixUsdTotal:sn(Object.values(m).reduce((E,R)=>E+R,0)),fixes:y,verdicts:g,pendingTaps:$,commits:s.length}}var aB,iB,Jm,HS,WS,qS,cB,zr,Xm,Zm,Ui,Bu,lB,uB,ta,KS,dB,mB,LS,hB,wB,gB,yB,bB,FS,kB,xB,vB,SB,Ym,Du,sn,zm,Uu=q(()=>{Pr();aB=sB(oB),iB=64*1024*1024,Jm=(e,t)=>aB("git",["-C",e,...t],{maxBuffer:iB,timeout:6e4}),HS="Bostrat-Epoch",WS="Bostrat-Model",qS="Bostrat-Fix-Cause",cB=24*36e5,zr=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Xm=90,Zm=3,Ui=Object.freeze(["defect","design","requirements","style"]),Bu="not-a-fix",lB=new Set(["A","B","E"]),uB=Object.freeze(["fix","likely-fix","planned","unknown"]),ta=new Set(["fix","likely-fix"]),KS=.8;dB={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};mB=/^revert\b|^revert "/i,LS=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,hB=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,wB=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;gB=/^([a-z]+)(\([^)]*\))?!?:\s/i,yB=new Set(["fix","hotfix","bugfix","revert"]),bB=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),FS=/\bBP-[A-Z]*\d+\b/i,kB=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);xB=[/(^|\/)\.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)\//],vB=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],SB=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Ym=e=>String(e??"").replace(/\s+/g," ").trim(),Du=400;sn=e=>Math.round(e*1e4)/1e4,zm=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as RB}from"node:child_process";import{promisify as OB}from"node:util";function JS(e,t=Date.now()){return jp(e,t)??0}function MB(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 YS(e,t,{now:n=Date.now()}={}){let r=JS(t,n),o=await K(e);return Lu({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:MB(o,t.name,r)})}function NB(e,t){let n=null;for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="turn.user"||o.boxId!==t||o.actor!=="user")continue;let s=o.payload||{};if(s.note?.kind!=="task"){if(o.source==="orchestrator"&&typeof s.text=="string"&&s.text.trim())return{text:s.text,kind:s.kind??null};!n&&typeof s.promptHead=="string"&&s.promptHead.trim()&&(n={text:s.promptHead,kind:s.kind??null})}}return n}function PB(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=NB(n,t.name),c=i?zn(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function DB(e,t){try{let{stdout:n}=await eh(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 jB(e){try{let{stdout:t}=await eh(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function XS(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 K(e);if(a.some(w=>w.type==="code.committed"&&w.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:w}=await eh(r,["show","-s","--format=%ct",n]),m=Number(w.trim())*1e3;Number.isFinite(m)&&m>0&&(i=m)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=JS(t,i),l=await DB(r,n),u=await jB(r),d=await C(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:u,...l,ts:i}}),p=null,f=!1;try{let w=await GS(r,n,{fixKey:ea({trackId:e.id,boxId:t.name,epoch:c}),window:zr,context:{...PB(e,t,a,c),ref:u,provider:t.agent??null}});if(w&&(p=await C(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:w}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&zS(w))){let g=Hi(w),b=w.origins.filter(x=>x.class==="rework").slice(0,3);await C(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:{sha:n,subject:w.subject,linesReworked:g,origins:b.map(x=>({key:x.key,provider:x.provider,model:x.model,lines:x.lines,ageDays:x.ageDays})),heuristic:w.heuristic,intent:w.intent,ts:i}}),f=!0}}catch(w){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${w?.message||w}`)}return{recorded:!0,event:d,rework:p,offered:f}}function ZS(e,t){let n=0;for(let r of e)r.payload?.sha===t&&(r.type==="rework.cause.answered"?n++:r.type==="rework.cause.retracted"&&n--);return n>0}async function QS(e,t,n){if(!Ui.includes(n)&&n!==Bu)throw new Error(`bad cause "${n}" (${Ui.join("|")}|${Bu})`);let r=await K(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(ZS(r,t))throw new Error("already answered");return C(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,...Array.isArray(o.payload?.shas)?{shas:o.payload.shas}:{},cause:n,level:"B",ts:Date.now()}})}async function eT(e,t){let n=await K(e);if(!ZS(n,t))throw new Error("no standing answer to retract");let r=[...n].reverse().find(o=>o.type==="rework.cause.answered"&&o.payload?.sha===t);return C(e,{type:"rework.cause.retracted",actor:"user",source:"orchestrator",boxId:r?.boxId??null,causationId:r?.id,payload:{sha:t,...Array.isArray(r?.payload?.shas)?{shas:r.payload.shas}:{},ts:Date.now()}})}async function Hu(e,{repo:t=null,number:n,title:r="",branch:o=null,url:s=null}={},{log:a=console,now:i=Date.now()}={}){try{if(!e?.id||n==null)return{offered:!1,reason:"incomplete"};let c=await K(e);if(c.some(x=>x.type==="rework.cause.offered"&&x.payload?.pr?.number===n&&(t==null||x.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let x of c)if(x.type==="rework.cause.offered")for(let y of Array.isArray(x.payload?.shas)?x.payload.shas:[x.payload?.sha])y&&l.add(y);let u=new Map;for(let x of c)x.type==="code.committed"&&x.payload?.sha&&u.set(x.payload.sha,x.payload.ref??null);let d=Qm({heuristic:Fu({subject:r}),subject:r,ref:o}),p=[];for(let x of c){if(x.type!=="code.rework")continue;let y=x.payload||{};if(!y.sha||l.has(y.sha)||y.codeDelta===!1||o&&u.has(y.sha)&&u.get(y.sha)!==o)continue;let $=y.intent?.intent??"unknown";(ta.has($)||$==="unknown"&&ta.has(d.intent))&&Hi(y)!==0&&p.push(y)}let f=p.reduce((x,y)=>x+Hi(y),0);if(f<Zm)return{offered:!1,reason:"below-threshold"};let w=new Map;for(let x of p)for(let y of x.origins??[]){if(y.class!=="rework")continue;let $=w.get(y.key)??{key:y.key,provider:y.provider??null,model:y.model??null,lines:0,ageDays:y.ageDays};$.lines+=y.lines,$.ageDays=Math.min($.ageDays,y.ageDays),w.set(y.key,$)}let m=p.map(x=>x.sha),g=p.map(x=>x.heuristic).find(x=>x&&x.level!=="C")??Fu({subject:r})??p.map(x=>x.heuristic).find(Boolean)??null,b=ta.has(d.intent)?d:p.find(x=>ta.has(x.intent?.intent))?.intent??d;return await C(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:null,payload:{sha:m[m.length-1],shas:m,pr:{repo:t,number:n,title:String(r||"").slice(0,200),url:s,branch:o},subject:String(r||"").slice(0,200)||p[p.length-1].subject,linesReworked:f,origins:[...w.values()].sort((x,y)=>y.lines-x.lines).slice(0,3),heuristic:g,intent:b,ts:i}}),{offered:!0,shas:m}}catch(c){return a.warn?.(`[rework] ${e?.id}: PR-level offer for #${n} failed: ${c?.message||c}`),{offered:!1,reason:"error"}}}var CB,eh,Wu=q(()=>{de();Gl();Uu();CB=OB(RB),eh=(e,t)=>CB("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as BB}from"node:child_process";import{promisify as LB}from"node:util";async function th(e,t,{midTurn:n=Lo,pollMs:r=FB,capMs:o=UB}={}){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 aT(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){tT.lastIndex=n;let o=tT.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(`
|
|
194
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+VB(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 nh(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function cT(e){let t=aT(e),n=t.match(KB);if(!n)return null;let r=nh(t,n.index);return zB.test(r)?null:{repo:r.match(iT)?.[1]??null,head:r.match(JB)?.[1]??null}}function lT(e){let t=aT(e),n=t.match(GB);if(!n)return null;let r=nh(t,n.index),o=nh(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("-")){XB.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(iT)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function QB(e,t,{exclude:n=()=>!1}={}){let r=async i=>{er("observer");let{stdout:c}=await nT("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",ZB],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw qr(l)&&Kr(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-HB&&!n(c)),s=await yn({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 eL(e,t){if(t)return[t];let n=[];for(let r of uT(e)){let o=await _e(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Wn().catch(()=>[])){let o=await _e(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function uT(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 tL(e,t,n){let r=await Qs(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)||YB(c.headRefName,e.id),i=[];for(let c of await eL(e,t?.repo??null)){let l;try{l=await QB(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await at(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 dT(){for(let e of[...rh])e()}function pT(e,t,{delays:n=rT,turnEnd:r}={}){let o=cT(t.command);if(!o)return null;let s=!!lT(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Vr.has(a))return null;Vr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(m=>{let g=()=>{clearTimeout(b),rh.delete(g),m()},b=setTimeout(g,l);rh.add(g)});let u=await tL(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Ct({repo:d,number:p.number,trackId:e.id,url:p.url,headRef:p.headRefName,title:p.title,source:"observed"});return!w&&!(f?.trackId===e.id&&await Yn(d,p.number))?null:(await th(t.box,t.agent,r),await C({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:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{Vr.delete(a)}})();return c.catch(()=>Vr.delete(a)),c}function fT(e,t,{delays:n=rT,turnEnd:r,openedWait:o}={}){let s=lT(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Vr.has(a))return null;Vr.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 w=uT(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let m of w){let g=await _e(m).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await Qs(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,m)=>{let g=await at(c,w.number);return g&&g.trackId!==e.id?null:(g||await Ct({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Ci(c,w.number),m&&((await K(e).catch(()=>[])).some(y=>y.type==="pr.state.changed"&&(y.payload?.change==="auto-armed"||y.payload?.change==="merged")&&y.payload?.number===w.number)||(await th(t.box,t.agent,r),await C({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:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let m=[];for(let M of d)try{er("observer");let{stdout:H}=await nT("gh",["pr","view",String(l),"--repo",M,"--json",nL],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await yn({slug:M})}}),P=JSON.parse(H||"null");P?.number&&m.push({slug:M,pr:P})}catch(H){qr(H)&&Kr(H)}let g=m.find(M=>M.pr.state==="MERGED")??(m.length===1?m[0]:null);if(!g)continue;c=g.slug;let b=g.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(f=b,s.auto)return p(b,!0);continue}let x=await at(c,b.number);if(x&&x.trackId!==e.id)return null;x||await Ct({repo:c,number:b.number,trackId:e.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await th(t.box,t.agent,r);let y=await K(e).catch(()=>[]);if(y.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="merged"&&M.payload?.number===b.number))return null;let $=M=>M.some(H=>H.type==="pr.state.changed"&&H.payload?.change==="opened"&&H.payload?.number===b.number),O=0;if(!$(y)){if(cT(t.command)&&await Yn(c,b.number)){let M=Date.now();O=M+1,await C({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:M,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await at(c,b.number))?.announced){let M=o?.capMs??WB,H=o?.pollMs??qB,P=Date.now()+M;for(;Date.now()<P&&(await new Promise(E=>setTimeout(E,H)),!$(await K(e).catch(()=>[]))););}}return await C({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(),O),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await Hu(e,{repo:c,number:b.number,title:b.title,branch:b.headRefName,url:b.url}).catch(()=>{}),await rr(c,b.number).catch(()=>{}),await nr(c,b.number).catch(()=>{}),await Hr(c,b.number,"merged"),b}return f?p(f,!1):null}finally{Vr.delete(a)}})();return i.catch(()=>Vr.delete(a)),i}var nT,rT,FB,UB,HB,WB,qB,oT,sT,KB,GB,zB,tT,VB,iT,JB,YB,XB,ZB,Vr,rh,nL,oh=q(()=>{de();kr();pt();Ni();Zn();or();Fi();Fo();Wu();nT=LB(BB),rT=[4e3,15e3,45e3],FB=2500,UB=5*6e4;HB=15e3,WB=9e4,qB=2e3,oT=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,sT=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,KB=new RegExp(`${oT}${sT}create\\b`),GB=new RegExp(`${oT}${sT}merge\\b`),zB=/\s--web\b/,tT=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,VB=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");iT=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,JB=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,YB=jS;XB=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);ZB="number,url,title,headRefName,createdAt";Vr=new Set,rh=new Set;nL="number,url,title,headRefName,state"});import rL from"node:fs/promises";import ah from"node:path";import oL from"node:os";function sh(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function qu(e){let t=sL.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=sh(n,"from"),s=o?o.replace(/^uds:/,""):null,a=mT(s);return{socket:s,pid:a,name:sh(n,"from-name"),mode:sh(n,"from-mode"),text:r.trim()}}function mT(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function aL(){return process.env.CLAUDE_CONFIG_DIR?ah.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):ah.join(oL.homedir(),".claude","sessions")}async function iL(e){let t=mT(e);if(!t)return null;try{let n=await rL.readFile(ah.join(aL(),`${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 hT(e,t){let n=await iL(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 sL,ih=q(()=>{sL=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function ch(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function lh({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!ch(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),wT);try{let a=await gT(`${$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 yT(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(!ch(s))return{ok:!1,source:"unconfigured"};let i=Ku.get(e.id)||0;if(a()-i<cL)return{ok:!1,source:"debounced"};Ku.set(e.id,a());let c=await Wi({env:s}),l=c.loops.find(p=>p.trackId===e.id);if(!l)return Ku.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 lh({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||Ku.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function Wi({env:e=process.env}={}){if(!ch(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),wT);try{let r=await gT(`${$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 wT,gT,cL,Ku,uh=q(()=>{pt();wT=1e4,gT=(...e)=>fetch(...e);cL=10*6e4,Ku=new Map});function bT(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function kT(e,t,n=dh){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await uL(`${$e(t)}${e}`,{headers:dL(t),signal:r.signal})}finally{clearTimeout(o)}}async function qi({env:e=process.env,force:t=!1,timeoutMs:n=dh}={}){if(!bT(e))return{followups:[],source:"unconfigured"};if(!t&&Uo&&Date.now()-Uo.at<lL)return{followups:Uo.followups,source:"cache"};try{let r=await kT("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return Uo={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Uo?{followups:Uo.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function xT(e,{env:t=process.env,timeoutMs:n=dh}={}){if(!e)return{followup:null,source:"unconfigured"};if(!bT(t))return{followup:null,source:"unconfigured"};try{let r=await kT(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=Uo?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var lL,dh,Uo,uL,dL,vT=q(()=>{pt();lL=6e4,dh=5e3,Uo=null,uL=(...e)=>fetch(...e);dL=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function Jr(e,t){if(ph.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}ph.add(e);try{return await t()}finally{ph.delete(e)}}var ph,Ki=q(()=>{ph=new Set});import{randomUUID as ra,createHash as pL}from"node:crypto";function ET(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function gL(e){for(let[t,n]of wL)if(t.test(String(e||"")))return`a standing prompt must not embed this Track's specifics (${n}) \u2014 the card supplies them at fire time; arm a one-shot with \`offer\` instead`;return null}function _T(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=na(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>ST)throw new Error(`label must be \u2264${ST} characters`);let o=na(n.trigger)||(n.ship?"now":""),s=t?mL:AT.filter(u=>u!=="track.opened");if(!s.includes(o))throw new Error(`trigger must be one of ${s.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let a=n.ship&&o==="now"?bL(n.ship):null,i=na(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>TT)throw new Error(`prompt must be \u2264${TT} bytes`);if(t){let u=gL(i);if(u)throw new Error(u)}let c=na(n.why).slice(0,fL)||null,l=t?yL(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function yL(e){if(e==null||e===""||e==="*")return null;let t=String(e).trim();if(!/^[\w.-]+\/[\w.-]+$/.test(t))throw new Error(`repo must be a forge slug like owner/name, got "${e}"`);return t}function bL(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):fh.slice();for(let i of n)if(!fh.includes(i))throw new Error(`ship.actions may only contain ${fh.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=na(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>na(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Gu(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}async function IT(e,t,{pull:n=qi,ship:r={}}={}){if(wh(e))throw new Error("follow-ups don't fire on a loop's run Track \u2014 report the outcome with `bostrat-loop report` instead");let o=_T(t);if(o.ship)return{kind:"ship",...await SL(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null};if(o.trigger==="now"){let g=ra(),b=ra();return await C(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:g,trigger:"now",label:o.label,prompt:o.prompt,why:o.why}}),await C(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:b,trigger:"now",repo:e.workspace?.repo||null,context:{},rows:[{armId:g,label:o.label,prompt:o.prompt,promptHash:mh(o.prompt),origin:"agent",why:o.why}]}}),{kind:"now",armId:g,offerId:b,...o}}let c=e.workspace?.repo||null,{followups:l}=await n().catch(()=>({followups:[]})),u=ET(o.label),d=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(d)return{kind:"covered",covered:{id:d.id,label:d.label},...o};let p=await K(s),f=Gu(p,{trigger:o.trigger}),w=f.length?f[f.length-1].armId:null,m=ra();return await C(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:m,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...w?{replaces:w}:{}}}),{kind:"armed",armId:m,replaces:w,...o}}async function $T(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!Gu(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await C(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function RT(e,t,{pull:n=qi}={}){if(wh(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=_T(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await K(s)).reduce((g,b)=>b.type==="followup.create.offered"?b.payload?.proposalId??g:b.type==="followup.create.answered"&&b.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=ET(r.label),d=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),p=d.find(g=>g.status==="deleted"),f=d.find(g=>g.status!=="deleted");if(f)throw new Error(`"${f.label}" (${hh[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let g=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${g} \u2014 do not re-propose it`)}let w=ra(),m=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await C(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:m?.agent??null,payload:{ts:Date.now(),proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function OT(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await K(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await C(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await C(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await C(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Gi(e,t,n={},{pull:r=qi}={}){if(!e||wh(e))return null;let o={id:e.id,cwd:e.cwd},s=await K(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null;if(t==="pr.merged"){let p=m=>{let g=(m?.prs||[]).filter(b=>Number.isFinite(Number(b?.number))).map(b=>`${b?.repo??m?.repo??""}#${Number(b.number)}`);return Number.isFinite(Number(m?.prNumber))&&g.push(`${m?.repo??""}#${Number(m.prNumber)}`),g},f=new Set(p(n));if(s.some(m=>m.type==="followup.offered"&&m.payload?.trigger==="pr.merged"&&p(m.payload?.context).some(g=>f.has(g))))return null}let i=Gu(s,{trigger:t}).map(p=>({armId:p.armId,label:p.label,prompt:p.prompt,promptHash:mh(p.prompt),origin:"agent",why:p.why??null})),c=[];if(t!=="now"){let{followups:p}=await r().catch(()=>({followups:[]}));c=p.filter(f=>f.status==="active"&&f.mode!=="off"&&f.trigger===t&&(f.repo==null||f.repo===a)).sort((f,w)=>{let m=g=>g.stats?.offered?(g.stats.accepted||0)/g.stats.offered:0;return m(w)-m(f)||(f.origin==="operator"?-1:0)-(w.origin==="operator"?-1:0)||(w.createdAt||0)-(f.createdAt||0)}).map(f=>({followupId:f.id,label:f.label,promptPreview:kL(f.prompt),promptHash:mh(f.prompt),origin:f.origin,repo:f.repo??null}))}let l=[...i,...c].slice(0,hL);if(!l.length)return null;let u=ra(),d={ts:Date.now(),offerId:u,trigger:t,repo:a,context:{...n,repo:a},rows:l};return await C(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:d}),d}function xL(e){let t=e?.context||{},n=Array.isArray(t.prs)&&t.prs.length?t.prs:t.prNumber?[{number:t.prNumber,title:t.prTitle,url:t.prUrl,repo:t.repo}]:[],r=o=>`PR #${o.number}${o.title?` "${o.title}"`:""}${o.url?` (${o.url})`:""}${o.repo?` on ${o.repo}`:""}`;switch(e?.trigger){case"pr.merged":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} merged:`:"Follow-up the operator tapped after this Track's PR merged:";case"pr.opened":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} opened:`:"Follow-up the operator tapped after this Track's PR opened:";case"checks.green":return n.length?`Follow-up the operator tapped after CI went green on ${n.map(r).join(" and ")}:`:"Follow-up the operator tapped after CI went green:";case"ship.done":return"Follow-up the operator tapped after the ship you just ran completed:";default:return"Follow-up the operator tapped:"}}async function CT(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){if(!t)throw new Error("offerId required");return Jr(`fu:${e.id}:${t}`,()=>vL(e,{offerId:t,followupId:n,armId:r,promoted:o},s))}async function vL(e,{offerId:t,followupId:n,armId:r,promoted:o},s){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=xT}=s,l={id:e.id,cwd:e.cwd},u=await K(l),d=u.find(y=>y.type==="followup.offered"&&y.payload?.offerId===t);if(!d)throw new Error("unknown offer");if(u.some(y=>y.type==="followup.answered"&&y.payload?.offerId===t))throw new Error("offer already answered");let p=d.payload.rows||[],f=y=>C(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...y}});if(!n&&!r)return await f({action:"dismiss",followupIds:p.map(y=>y.followupId).filter(Boolean),armIds:p.map(y=>y.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let w=null,m=null;if(n){if(!p.some(O=>O.followupId===n))throw new Error("that follow-up is not on this card");let{followup:y,source:$}=await c(n);if($==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!y||y.status!=="active"){await f({action:"removed",followupId:n});let O=new Error("removed in Settings since it was offered");throw O.code="removed",O}w=y.prompt,m=y.label}else{let y=p.find(O=>O.armId===r);if(!y)throw new Error("that arm is not on this card");if(w=u.find(O=>O.type==="followup.armed"&&O.payload?.armId===r)?.payload?.prompt??y.prompt,m=y.label,!w)throw new Error("the arm's prompt is missing from the log")}let g=!1;if(e.archivedAt){if(!i)throw new Error("this Track is archived");await i(e.id,{freshSession:!0}),g=!0}if(!a)throw new Error("sayToLead unavailable");let b=`${xL(d.payload)}
|
|
193
195
|
|
|
194
|
-
${
|
|
196
|
+
${w}`,x=await a(e.id,b,{kind:"followup-go"});return await f({action:"sent",...n?{followupId:n}:{armId:r},label:m,...o?{promoted:String(o)}:{},...g?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:g,queued:!!x?.queued}}async function SL(e,t,{changedRepos:n=null,probe:r=Mu,branchOf:o=Gr}={}){let s=t.ship;if(!e.workspace?.branch)throw new Error("this Track has no branch to ship \u2014 a ship offer needs a worktree Track");let a={id:e.id,cwd:e.cwd},i=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,c=e.workspace?.kind==="multi",l;if(c&&n){let g=await n(e).catch(()=>[]),b=s.repos?g.filter(x=>s.repos.includes(x.name)):g;l=b.length?b.map(x=>({name:x.name,slug:x.repo??null})):void 0}let u=(c?null:await o(e.cwd).catch(()=>null))||e.workspace.branch,d=s.rebase==="offered",p=await r(e,{branch:u,rebase:d}).catch(()=>null),f=[...d?["rebase"]:[],...s.actions].filter(g=>Fr.includes(g));if(p&&p.available.length===0)return{skipped:!0,reason:"nothing to ship \u2014 the tree is clean, pushed, and its PR is open",shipState:p};let w=ra(),m=t.why?`${t.label} \u2014 ${t.why}`:t.label;return await C(a,{type:"pr.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:i?.agent??null,payload:{offerId:w,ts:Date.now(),excerpt:m,via:"cli",branch:u,base:s.base||e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:"lead",available:p?.available,defaults:p?p.defaults.filter(g=>f.includes(g)||g==="rebase"):void 0,prState:p?.prState,openPr:p?.openPr||void 0,repos:p?.repos||l,offered:p?f.filter(g=>p.available.includes(g)):f,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:w,branch:u,offered:f,available:p?.available??null,prState:p?.prState??null}}function MT(e){let t=!1;for(let n of e)n.type==="pr.offered"&&(n.source==="cli"||n.payload?.via==="cli")?t=!0:(n.type==="turn.user"&&!n.payload?.kind||n.type==="pr.offer.answered")&&(t=!1);return t}function NT(e){let t=null;for(let n of e){let r=n.payload||{};n.type==="pr.offer.answered"?t=r.action&&r.action!=="dismiss"&&r.action!=="pr-exists"?r.offerId:null:n.type==="pr.exists.answered"?t=r.choice&&r.choice!=="dismiss"?r.offerId??t:null:(n.type==="turn.user"&&!r.kind||n.type==="followup.offered"&&r.trigger==="ship.done"&&r.context?.shipOfferId&&r.context.shipOfferId===t)&&(t=null)}return t}async function PT(e,{pull:t=qi}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await K({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?Gu(s):[]}}var ST,TT,fL,AT,mL,hh,hL,fh,mh,na,wL,wh,kL,zu=q(()=>{de();Mo();Km();Fo();vT();Ki();ST=60,TT=8*1024,fL=200,AT=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],mL=AT.filter(e=>e!=="now"&&e!=="track.opened"),hh={now:"now","pr.merged":"after merge","ship.done":"after ship","pr.opened":"after PR opens","checks.green":"after CI goes green","track.opened":"when a Track opens"},hL=3,fh=["commit","push","pr"];mh=e=>pL("sha256").update(String(e)).digest("hex").slice(0,16),na=e=>typeof e=="string"?e.trim():"",wL=[[/(^|[^\w&])#\d+\b/,"a PR/issue number"],[/https?:\/\/\S+/i,"a URL"],[/\b[0-9a-f]{7,40}\b/i,"a commit SHA"],[/\bv\d+\.\d+\.\d+\b/,"a release tag"]];wh=e=>String(e?.name||"").startsWith("\u21BB");kL=e=>String(e||"").split(`
|
|
195
197
|
`).slice(0,3).join(`
|
|
196
|
-
`).slice(0,400)});import{randomUUID as
|
|
197
|
-
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;t.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return t}async function CB(e,t){let n=`${e.id}:${t.name}`;if(!lh.has(n)){let r=await G(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);lh.set(n,o)}return lh.get(n)}async function bT(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await mo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await CB(e,t),o=OB(await _t(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 C(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:vn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await C(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 lh,kT=q(()=>{fe();wo();rn();qn();lh=new Map});import Lu from"node:fs";import dh from"node:path";import MB from"node:os";async function DB({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(PB,{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:xT})});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<xT)break}return r}function jB(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 ST(){try{return JSON.parse(Lu.readFileSync(Fu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function LB(e){try{Lu.mkdirSync(dh.dirname(Fu()),{recursive:!0});let t=Fu()+".tmp";Lu.writeFileSync(t,JSON.stringify(e)),Lu.renameSync(t,Fu())}catch{}}async function UB({fetchImpl:e,now:t=Date.now()}={}){if(qr.rows&&t-qr.at<BB)return qr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=ST(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await DB({sinceMs:o,cookie:n,fetchImpl:e});qr={at:t,rows:s.map(jB).filter(Boolean),error:null}}catch(s){qr={at:t,rows:null,error:String(s?.message||s)}}return qr.rows}async function HB(e,t){let n=`${e.id}:${t.name}`;if(!uh.has(n)){let r=await G(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);uh.set(n,o)}return uh.get(n)}async function TT(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!vT())return;let r=await UB({fetchImpl:n});if(!r||!r.length)return;let o=await ux(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await HB(e,t),a=ST(),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,p=l.vendorCents!=null?l.vendorCents/100:0;!p&&d&&(p=vn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await C(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,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}})}LB({sinceMs:c,seen:[...i].slice(-FB)})}catch{}}var NB,vT,PB,xT,BB,qr,Fu,FB,ph,uh,fh=q(()=>{fe();Wf();qn();NB=process.env.BOSTRAT_STATE_DIR||dh.join(process.env.HOME||MB.tmpdir(),".bostrat"),vT=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,PB="https://cursor.com/api/dashboard/get-filtered-usage-events",xT=100;BB=5*6e4,qr={at:0,rows:null,error:null},Fu=()=>dh.join(NB,"cursor-usage.json");FB=5e3;ph=()=>({enabled:vT(),lastPollAt:qr.at||null,error:qr.error}),uh=new Map});import Gi from"node:fs/promises";import It from"node:path";import WB from"node:os";import qB from"node:net";import{randomUUID as GB}from"node:crypto";import{execFile as KB}from"node:child_process";import{promisify as VB}from"node:util";function eL(e){for(let t of kh(e))if(!RT.test(t))return t;return null}function OT(e){let t=kh(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function tL(e,t){let n=OT(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return It.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return It.resolve(e,n[r+1])}return e}function rL(e){let t=!1,n=!1;for(let r of kh(e)){if(!t){if(RT.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&mh(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,mh(r))return`serves the filesystem root ("${r}")`;continue}if(mh(r))return`serves the filesystem root ("${r}")`;if(nL(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function sL(e,t){if(It.resolve(e)===It.resolve(t))return!0;let[n,r]=await Promise.all([Uu(e),Uu(t)]);return n===r}async function aL(e){let n=(await Gi.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 CT(It.join(e,o.name,gh))?`./${o.name}`:null))).filter(Boolean)}async function iL(e,t){let[n,r]=await Promise.all([Uu(e),Uu(t)]),o=It.relative(n,r);if(o&&(o.startsWith("..")||It.isAbsolute(o)))throw Wi(`serve directory escapes the Track workspace: ${t}`)}async function cL(e,t){let n=rL(t);if(n)throw Wi(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await oL(e))throw Wi(`serve directory does not exist: ${e}`);let r=eL(t);if(!r)return;if(!r.includes("/")&&!await Wb(r,{loginShell:!0,failOpen:!0}))throw Wi(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(XB.test(t)||ZB.has(r)||!JB.has(r)||!OT(t).includes("run")||QB.test(t))return;let o=tL(e,t);if(await CT(It.join(o,gh)))return;let s=await aL(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Wi(`no ${gh} in ${o}${a}`)}async function rr(){return En||(hh||=(async()=>{try{En=JSON.parse(await Gi.readFile($T,"utf8"))}catch{En={views:[],adoptWatermarks:{}}}finally{hh=null}return En.views||=[],En.adoptWatermarks||={},En})(),hh)}async function In(){await Et($T,En)}function nr(e,t,n={}){return C({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 vh(e){return qi.get(e)||null}function NT(e,{name:t,box:n}={}){let r=qi.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 qi.set(e,o),o}function yh(e,t){let n=qi.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Hu(e,{error:t=null}={}){let n=qi.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function PT(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 bh(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function lL(e,t){let n=bh(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!_T.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!_T.has(s))return s}}function Ki(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=qB.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 Sh(e){if(process.platform!=="darwin"){await _n("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await _n("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 DT(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function uL(e,t){let n=new Set(e.views.map(o=>o.port)),r=DT();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=AT;o<=ET;o++)if(!(n.has(o)||r.has(o))&&!await Ki(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${AT}-${ET}`)}async function dL(e){try{let{stdout:t}=await _n(X,Z("list-sessions","-F","#{session_name}"));return t.split(`
|
|
198
|
-
`).some(n=>n.trim()===
|
|
198
|
+
`).slice(0,400)});import{randomUUID as TL}from"node:crypto";function zi(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function DT(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function jT(e){return`job|${e.box}|id:${e.id}`}async function yh(e){return gh.has(e.id)?gh.get(e.id):(Vu.has(e.id)||Vu.set(e.id,K(e).then(t=>{let n={keys:new Set,cmdN:new Map};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(zi({role:o,sourceId:r.payload.sourceId})),n.keys.add(zi({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(zi({role:"user",sourceId:r.payload.sourceId})),n.keys.add(zi({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(DT({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>(n.cmdN.get(r.boxId)??0)&&n.cmdN.set(r.boxId,Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(jT({id:r.payload.id,box:r.boxId}));return gh.set(e.id,n),n}).finally(()=>Vu.delete(e.id))),Vu.get(e.id))}async function BT(e,t){let n=new Set;try{let r=(await yh(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=zi(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?qu(o.text):null;if(a){await C(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 hT(a,Vi),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=Nk(o);if(i){await C(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}}),yT(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 C(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=Fv(o.text);if(c&&MT(await K(e).catch(()=>[])))continue;if(c){let l=Wv(c),u;if(e.workspace?.kind==="multi"){let w=await Ho(e).catch(()=>[]);u=w.length?w.map(m=>({name:m.name,slug:m.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await Gr(e.cwd).catch(()=>null))||e.workspace.branch,p=await Mu(e,{branch:d,rebase:l}).catch(()=>null),f=p?null:Hv(c);if(p&&p.available.length===0)continue;await C(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:TL(),ts:o.ts,excerpt:c,branch:d,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:p?.available,defaults:p?.defaults,prState:p?.prState,openPr:p?.openPr||void 0,repos:p?.repos||u,offered:p?.defaults||f?.offered||void 0,toPr:f?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function LT(e,t){try{let n=await yh(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=DT(o);if(n.keys.has(s))continue;n.keys.add(s);let a=(n.cmdN.get(o.box)??0)+1;n.cmdN.set(o.box,a),await C(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:a}}),pT(e,o),fT(e,o)}}catch{}}async function FT(e,t){try{let n=await yh(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=jT(o);n.keys.has(s)||(n.keys.add(s),await C(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 gh,Vu,UT=q(()=>{de();Mo();Km();oh();Fo();ih();or();Bl();uh();zu();gh=new Map,Vu=new Map});function AL(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
199
|
+
`)){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 EL(e,t){let n=`${e.id}:${t.name}`;if(!bh.has(n)){let r=await K(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);bh.set(n,o)}return bh.get(n)}async function HT(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await ko(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await EL(e,t),o=AL(await _t(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 C(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Tn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await C(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 bh,WT=q(()=>{de();vo();nn();Vn();bh=new Map});import Ju from"node:fs";import xh from"node:path";import _L from"node:os";async function RL({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n($L,{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:qT})});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<qT)break}return r}function OL(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 GT(){try{return JSON.parse(Ju.readFileSync(Yu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function ML(e){try{Ju.mkdirSync(xh.dirname(Yu()),{recursive:!0});let t=Yu()+".tmp";Ju.writeFileSync(t,JSON.stringify(e)),Ju.renameSync(t,Yu())}catch{}}async function PL({fetchImpl:e,now:t=Date.now()}={}){if(Yr.rows&&t-Yr.at<CL)return Yr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=GT(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await RL({sinceMs:o,cookie:n,fetchImpl:e});Yr={at:t,rows:s.map(OL).filter(Boolean),error:null}}catch(s){Yr={at:t,rows:null,error:String(s?.message||s)}}return Yr.rows}async function DL(e,t){let n=`${e.id}:${t.name}`;if(!kh.has(n)){let r=await K(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);kh.set(n,o)}return kh.get(n)}async function zT(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!KT())return;let r=await PL({fetchImpl:n});if(!r||!r.length)return;let o=await Mx(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await DL(e,t),a=GT(),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,p=l.vendorCents!=null?l.vendorCents/100:0;!p&&d&&(p=Tn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await C(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,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}})}ML({sinceMs:c,seen:[...i].slice(-NL)})}catch{}}var IL,KT,$L,qT,CL,Yr,Yu,NL,vh,kh,Sh=q(()=>{de();Qf();Vn();IL=process.env.BOSTRAT_STATE_DIR||xh.join(process.env.HOME||_L.tmpdir(),".bostrat"),KT=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,$L="https://cursor.com/api/dashboard/get-filtered-usage-events",qT=100;CL=5*6e4,Yr={at:0,rows:null,error:null},Yu=()=>xh.join(IL,"cursor-usage.json");NL=5e3;vh=()=>({enabled:KT(),lastPollAt:Yr.at||null,error:Yr.error}),kh=new Map});import{randomUUID as jL,createHmac as BL}from"node:crypto";function JT(e){Ah=typeof e=="string"&&e?e:null}function LL(e){if(!Ah)return null;let t=He(e);return t?BL("sha256",Buffer.from(Ah,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function ZT(){let e={};for(let t of XT)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function VT(e,t){let n=Math.max(0,Number(t)||0);for(let r of XT)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function QT(){let e={};for(let t of YT)e[t]=0;return e}function UL(){In={total:new Map,holds:QT(),queueWaitMs:ZT(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function eA({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=In;return{total:[...n.total].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,producer:a,outcome:i,n:o}}),holds:{...n.holds},queueWaitMs:{buckets:{...n.queueWaitMs.buckets},sumMs:n.queueWaitMs.sumMs,count:n.queueWaitMs.count},readyWaitMs:[...n.readyWaitMs].map(([r,o])=>{let[s,a]=r.split("|");return{provider:s,outcome:a,buckets:{...o.buckets},sumMs:o.sumMs,count:o.count}}),verify:[...n.verify].map(([r,o])=>{let[s,a]=r.split("|");return{provider:s,verdict:a,n:o}}),retries:[...n.retries].map(([r,o])=>({provider:r,n:o})),queueDepth:Number(e)||0,queueOldestAgeMs:Number(t)||0}}function Ji({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:FL.has(t)?t:"composer"}function Yi({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:jL(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:LL(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:QT(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function Xi(e,t,{position:n=null,reason:r="busy"}={}){!t||!e||(t.queuedAt=Date.now(),await C(e,{type:"delivery.queued",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:{ts:t.queuedAt,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},reason:r,position:n,textBytes:t.textBytes,textHmac16:t.textHmac16,requeued:t.requeued}}).catch(()=>{}))}function tA(e,t){YT.includes(t)&&(e?.holds&&(e.holds[t]+=1),In.holds[t]+=1)}function Zi(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}async function $n(e,t,n,{now:r=Date.now}={}){if(!t||t.settled)return null;t.settled=!0,t.outcome=n;let o=r(),s=t.provider||"unknown",a=t.queuedAt!=null,i=a?Math.max(0,(t.dequeuedAt??o)-t.queuedAt):null;if(Th(In.total,`${s}|${t.producer}|${n}`),a&&VT(In.queueWaitMs,i),t.readyWaitMs!=null){let l=`${s}|${t.readyOutcome||"unknown"}`;In.readyWaitMs.has(l)||In.readyWaitMs.set(l,ZT()),VT(In.readyWaitMs.get(l),t.readyWaitMs)}t.verify?.verdict&&(Th(In.verify,`${s}|${t.verify.verdict}`),t.verify.retries&&Th(In.retries,s,t.verify.retries));let c={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:null,textBytes:t.textBytes,textHmac16:t.textHmac16,paste:t.paste,attachments:t.attachments,outcome:n,queued:a,queueWaitMs:i,queueHolds:{...t.holds},requeued:t.requeued,readyWaitMs:t.readyWaitMs,readyOutcome:t.readyOutcome,verify:t.verify?{...t.verify}:null,receiptMs:null,totalMs:Math.max(0,o-t.t0)};return e&&await C(e,{type:"delivery.settled",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:c}).catch(()=>{}),c}function oa(e){if(e?.usageCap)return"refused:cap";if(e?.interrupted)return"interrupted";if(e?.code)return`failed:${e.code}`;let t=String(e?.message||"");return/not running/i.test(t)?"failed:not-running":/archived/i.test(t)?"failed:archived":/blocked on an interactive prompt|isn't signed in|is waiting on its|stuck on its update/i.test(t)?"failed:blocked":"failed:error"}var Ah,YT,XT,FL,In,Th,Eh=q(()=>{de();yi();Ah=null;YT=["busy","prompt","handoff","noSession","cap","grace"],XT=[500,2e3,1e4,6e4,6e5],FL=new Set(["composer","voice","loop"]);In=null;UL();Th=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import tc from"node:fs/promises";import It from"node:path";import HL from"node:os";import WL from"node:net";import{randomUUID as qL}from"node:crypto";import{execFile as KL}from"node:child_process";import{promisify as GL}from"node:util";function QL(e){for(let t of Mh(e))if(!iA.test(t))return t;return null}function cA(e){let t=Mh(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function eF(e,t){let n=cA(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return It.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return It.resolve(e,n[r+1])}return e}function nF(e){let t=!1,n=!1;for(let r of Mh(e)){if(!t){if(iA.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&_h(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,_h(r))return`serves the filesystem root ("${r}")`;continue}if(_h(r))return`serves the filesystem root ("${r}")`;if(tF(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function oF(e,t){if(It.resolve(e)===It.resolve(t))return!0;let[n,r]=await Promise.all([Xu(e),Xu(t)]);return n===r}async function sF(e){let n=(await tc.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 lA(It.join(e,o.name,Rh))?`./${o.name}`:null))).filter(Boolean)}async function aF(e,t){let[n,r]=await Promise.all([Xu(e),Xu(t)]),o=It.relative(n,r);if(o&&(o.startsWith("..")||It.isAbsolute(o)))throw Qi(`serve directory escapes the Track workspace: ${t}`)}async function iF(e,t){let n=nF(t);if(n)throw Qi(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await rF(e))throw Qi(`serve directory does not exist: ${e}`);let r=QL(t);if(!r)return;if(!r.includes("/")&&!await ik(r,{loginShell:!0,failOpen:!0}))throw Qi(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(YL.test(t)||XL.has(r)||!JL.has(r)||!cA(t).includes("run")||ZL.test(t))return;let o=eF(e,t);if(await lA(It.join(o,Rh)))return;let s=await sF(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Qi(`no ${Rh} in ${o}${a}`)}async function ir(){return Rn||(Ih||=(async()=>{try{Rn=JSON.parse(await tc.readFile(aA,"utf8"))}catch{Rn={views:[],adoptWatermarks:{}}}finally{Ih=null}return Rn.views||=[],Rn.adoptWatermarks||={},Rn})(),Ih)}async function Cn(){await Et(aA,Rn)}function ar(e,t,n={}){return C({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 Ph(e){return ec.get(e)||null}function dA(e,{name:t,box:n}={}){let r=ec.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 ec.set(e,o),o}function Oh(e,t){let n=ec.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Zu(e,{error:t=null}={}){let n=ec.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function pA(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 Ch(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function cF(e,t){let n=Ch(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!oA.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!oA.has(s))return s}}function nc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=WL.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 Dh(e){if(process.platform!=="darwin"){await On("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await On("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 fA(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function lF(e,t){let n=new Set(e.views.map(o=>o.port)),r=fA();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=nA;o<=rA;o++)if(!(n.has(o)||r.has(o))&&!await nc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${nA}-${rA}`)}async function uF(e){try{let{stdout:t}=await On(X,Z("list-sessions","-F","#{session_name}"));return t.split(`
|
|
200
|
+
`).some(n=>n.trim()===sa(e))}catch{return!1}}async function rc(e){try{await On(X,Z("kill-session","-t",sa(e)))}catch{}}async function jh(e){if(!await si())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=sa(e.slug),n=e.serveCwd??e.cwd,r=It.dirname(process.execPath);await On(X,Z("new-session","-d","-s",t,"-c",n)),await On(X,Z("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await On(X,Z("send-keys","-t",t,"Enter")),await On(X,Z("send-keys","-t",t,"-l",e.cmd)),await On(X,Z("send-keys","-t",t,"Enter"))}async function sA(e,t=12){try{let{stdout:n}=await On(X,Z("capture-pane","-t",sa(e),"-p"));return String(n||"").trimEnd().split(`
|
|
199
201
|
`).filter(Boolean).slice(-t).join(`
|
|
200
|
-
`)}catch{return""}}async function fL(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await dL(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await IT(e.slug)||"server exited before listening"),tr.delete(e.id),await nr(e,e.state,e.error?{error:e.error}:{}),!0}if(await Ki(e.port))return tr.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await nr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>pL?(e.state="error",e.error=await IT(e.slug)||"never started listening",await nr(e,"error",{error:e.error}),!0):!1;let o=(tr.get(e.id)||0)+1;return tr.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Wu(){if(!wh){wh=!0;try{let e=await rr(),t=!1;for(let n of e.views)await fL(n)&&(t=!0);t&&await In()}finally{wh=!1}}}async function qu({trackId:e}={}){await Wu();let t=await rr();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function jT(e){let n=(await rr()).views.find(r=>r.id===e);return n?{...n}:null}function Ah(e){return En&&En.views.find(t=>t.slug===e)||null}async function Eh({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=It.resolve(r,o??"."),d=i==="detected"?null:async()=>{await iL(r,u),await cL(u,n)},p=await rr();if(i!=="detected"){let w=bh(t),g=String(n).trim(),k=p.views.find(x=>x.trackId===e&&(x.slug===w||bh(x.name)===w||x.cmd===g&&It.resolve(x.serveCwd??x.cwd)===u));if(k)return mL(p,k,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(w=>w.state==="live"||w.state==="starting").length>=Gr)throw new Error(`view cap reached (${Gr} live) \u2014 stop one first`);let h={id:GB().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:lL(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await uL(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Th(h),p.views.push(h),await In(),await nr(h,"created",{origin:i}),yh(e,h.id),i==="detected"&&await Ki(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await In(),await nr(h,"live")),{...h}}async function mL(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 sL(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Ki(t.port))return await In(),yh(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>=Gr)throw new Error(`view cap reached (${Gr} live) \u2014 stop one first`);return t.origin==="detected"?(await Sh(t.port),t.origin="agent"):await Vi(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),tr.delete(t.id),await Th(t),await In(),await nr(t,"created",{restarted:!0,reused:!0}),yh(t.trackId,t.id),{...t,reused:!0}}async function BT(e){let n=(await rr()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Sh(n.port):await Vi(n.slug),n.state!=="stopped"&&(n.state="stopped",tr.delete(n.id),await In(),await nr(n,"stopped")),{...n}}async function LT(e){let t=await rr(),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>=Gr)throw new Error(`view cap reached (${Gr} live) \u2014 stop one first`);return await Vi(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),tr.delete(n.id),await Th(n),await In(),await nr(n,"created",{restarted:!0}),{...n}}async function FT(e){let t=await rr(),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 Sh(r.port):await Vi(r.slug),t.views.splice(n,1),tr.delete(r.id),await In(),await nr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function UT(e){let t=await rr(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Vi(r.slug),tr.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await In()),n.length}async function HT(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await rr(),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 In()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(hL)){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(!DT().has(c)&&!n.views.some(l=>l.port===c)&&await Ki(c))try{i.push(await Eh({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 _n,zB,$T,AT,ET,Gr,YB,ea,_T,Wi,JB,gh,XB,ZB,QB,kh,RT,mh,nL,oL,CT,Uu,En,hh,qi,xh,MT,tr,pL,wh,hL,Gu=q(()=>{dt();fe();gr();_n=VB(KB),zB=process.env.BOSTRAT_STATE_DIR||It.join(process.env.HOME||WB.tmpdir(),".bostrat"),$T=It.join(zB,"views.json"),[AT,ET]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Gr=Number(process.env.BOSTRAT_VIEW_MAX)||3,YB="bostrat-view-",ea=e=>`${YB}${e}`,_T=new Set(["bostrat","www","api","views","app"]),Wi=e=>Object.assign(new Error(e),{status:400}),JB=new Set(["npm","pnpm","yarn","bun"]),gh="package.json",XB=/[|;&`]|\$\(/,ZB=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),QB=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,kh=e=>String(e).trim().split(/\s+/).filter(Boolean),RT=/^[A-Za-z_][A-Za-z0-9_]*=/;mh=e=>e==="/",nL=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");oL=e=>Gi.stat(e).then(t=>t.isDirectory(),()=>!1),CT=e=>Gi.stat(e).then(t=>t.isFile(),()=>!1),Uu=e=>Gi.realpath(e).catch(()=>It.resolve(e));En=null,hh=null;qi=new Map,xh=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,MT=12e3;tr=new Map,pL=12e4;wh=!1;hL=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Ku(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>gL?null:n}function yL(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Vu(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await G(e)}catch{return}let o=Ku(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await Mo(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(_h.get(e.id)||0)<wL)return;_h.set(e.id,a);let i;try{i=await Tn(e.cwd,{caller:"reconcile"})}catch{return}let c=await Ys(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await _e(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Ct({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!p&&!(d?.trackId===e.id&&await Kn(u,l.number))){if(!yL(d,o))return;let f=await G(e).catch(()=>null);if(f&&Ku(f)==null)return;await C(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:l.number,url:l.url,title:l.title,branch:n,repo:u}});return}}await C(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),_h.delete(e.id)}var wL,_h,gL,zu=q(()=>{fe();Co();hr();zn();Ni();No();wL=6e3,_h=new Map,gL=30*6e4});import Ih from"node:os";import{execFile as bL}from"node:child_process";import{promisify as kL}from"node:util";async function Yu(){if(process.platform!=="darwin")return Ih.freemem();try{let{stdout:e}=await xL("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:Ih.freemem()}catch{return Ih.freemem()}}var xL,$h=q(()=>{xL=kL(bL)});import Je from"node:fs/promises";import Ze from"node:path";import KT from"node:os";import{randomUUID as vL}from"node:crypto";import{execFile as SL}from"node:child_process";import{promisify as TL}from"node:util";import{chromium as AL}from"playwright-core";async function On(){return jo||(Rh||=(async()=>{try{jo=JSON.parse(await Je.readFile(zT,"utf8"))}catch{jo={mocks:[]}}finally{Rh=null}return jo.mocks||=[],jo})(),Rh)}async function Kt(){await Et(zT,jo)}function ZT(e,t){Rn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Rn(e,t.v,"png")}function kt(e,t,n={}){return C({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 Vt(e,t){let n=Yi(e.cwd);return{html:Ze.join(n,`${e.id}.v${t}.html`),png:Ze.join(n,`${e.id}.v${t}.png`)}}function zi(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Ze.join(e.cwd,r):null}function cn(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 Mh(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Ju(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(CL)}function ML(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 NL(e){let t=Buffer.alloc(24),n=await Je.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function PL({imgW:e,imgH:t,bbox:n,pad:r,align:o=1}){if(!n||!(e>0)||!(t>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=e*.9&&a>=t*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function DL(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,p=-1,f=-1;for(let h=0;h<s;h++)for(let w=h*o*4,g=0;g<o;g++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(g<u&&(u=g),g>p&&(p=g),h<d&&(d=h),f=h);return p<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:p,bottom:f}}},t.toString("base64"))}async function jL(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,p)=>{c.onload=d,c.onerror=()=>p(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 BL({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await AL.launch({executablePath:YT,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Mt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:ta})).newPage();a.setDefaultTimeout(na),await a.goto(`file://${e}`,{waitUntil:"load",timeout:na}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:na});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await DL(a,i),u=l&&PL({...l,pad:IL*ta,align:ta});u&&(i=await jL(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<JT)throw new Mt(`capture produced a ${i.length}-byte image`);let c=Mh(i);if(!c)throw new Mt("output is not a PNG");return await Je.writeFile(t,i),{width:Math.round(c.width/ta),height:Math.round(c.height/ta),dpr:ta,renderMs:Date.now()-r}}catch(s){throw s instanceof Mt?s:new Mt(/timeout/i.test(s?.message||"")?`render timed out after ${na}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function GT({htmlPath:e,outPath:t,width:n}){let r=await Je.mkdtemp(Ze.join(KT.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Ch(WT,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:na,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Mt(`firefox not found (${WT})`,""):new Mt(c?.killed?`render timed out after ${na}ms`:"firefox exited non-zero",s)}let a=await Je.stat(t).catch(()=>null);if(!a||a.size<JT)throw new Mt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Mh(await NL(t));if(!i)throw new Mt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Je.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function LL(e){if(EL==="firefox")return GT(e);try{return await BL(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await GT(e)}catch(n){throw/not found/.test(n?.message||"")?new Mt(`no browser installed \u2014 chromium missing at ${YT} 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 QT(e){return Oh=Oh.then(e,e),Oh}async function eA(e,t){let n=await On(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Kt(),await kt(r,"rendering",{version:t});try{let o=await Yu();if(o<$L)throw new Mt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Vt(r,t),a=await FL({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Je.rm(s.png,{force:!0}).catch(()=>{});return}Nh(r,{v:t,source:"html",htmlRel:Ze.relative(r.cwd,s.html),pngRel:Ze.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Kt(),await kt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),$n(r),Rn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Kt(),await kt(r,"error",{version:t,error:r.error}),$n(r)}}}function Nh(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 UL({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?_L:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Ji({trackId:e,group:t}={}){let n=await On(),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 or(e){let n=(await On()).mocks.find(r=>r.id===e);return n?{...n}:null}function tA({html:e,image:t,video:n}){if([typeof e=="string"&&e.trim(),!!t,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(t&&!Ju(t))throw new Error("image must be a PNG");if(n){let o=ML(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>qT)throw new Error(`video must be ${qT/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function nA({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=UL({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=tA({html:n,image:r,video:o}),h=await On(),w={id:vL().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:p?"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 Je.mkdir(Yi(s),{recursive:!0}),h.mocks.push(w),p?(await Je.writeFile(Vt(w,1).html,n),await Kt(),await kt(w,"created"),$n(w),Rn(w,1,"html"),QT(()=>eA(w.id,1))):o?(await oA(w,1,o,f),await Kt(),await kt(w,"created"),await kt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),$n(w),ZT(w,w.versions[0])):(await sA(w,1,r),await Kt(),await kt(w,"created"),await kt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),$n(w),Rn(w,1,"png")),{...w}}async function rA(e,{html:t,image:n,video:r,width:o}={}){let a=(await On()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=tA({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 Je.mkdir(Yi(a.cwd),{recursive:!0}),i?(a.state="queued",await Je.writeFile(Vt(a,l).html,t),await Kt(),await kt(a,"updated",{version:l}),$n(a),Rn(a,l,"html"),QT(()=>eA(a.id,l))):r?(await oA(a,l,r,c),await Kt(),await kt(a,"updated",{version:l}),await kt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:cn(a)?.durationS??null}),$n(a),ZT(a,a.versions.find(u=>u.v===l))):(await sA(a,l,n),await Kt(),await kt(a,"updated",{version:l}),await kt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),$n(a),Rn(a,l,"png")),{...a}}async function HL(e){try{let{stdout:t}=await Ch(RL,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:XT,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 WL(e,t){try{await Ch(OL,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:XT,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Je.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function oA(e,t,n,r){let o=Yi(e.cwd),s=Ze.join(o,`${e.id}.v${t}.${r.ext}`),a=Ze.join(o,`${e.id}.v${t}.poster.png`);await Je.writeFile(s,n);let i=await HL(s),c=await WL(s,a);Nh(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Ze.relative(e.cwd,s),posterRel:c?Ze.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function sA(e,t,n){let r=Mh(n);if(!r)throw new Error("image must be a PNG");let o=Vt(e,t);await Je.writeFile(o.png,n),Nh(e,{v:t,source:"image",htmlRel:null,pngRel:Ze.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function aA(e){let t=await On(),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 kt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function iA(e){let t=await On(),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=Vt(r,o);await Je.rm(s.html,{force:!0}).catch(()=>{}),await Je.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Je.rm(Ze.join(r.cwd,s),{force:!0}).catch(()=>{});return await Kt(),await kt(r,"deleted"),Qt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function cA(e){let n=(await On()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Ze.join(VT,"mock-archive",e.id),o=Yi(e.cwd),s=Ze.join(r,".bostrat","mocks");await Je.mkdir(s,{recursive:!0});let a=await Je.readdir(o).catch(()=>[]);for(let i of a)await Je.copyFile(Ze.join(o,i),Ze.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Je.stat(Ze.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),$n(i)}return await Kt(),n.length}async function lA(e){let t=await On(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Kt();for(let r of n)Qt("mocks",r.id).catch(()=>{})}return n.length}async function uA(){if(!Le())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await On();for(let r of n.mocks){await $n(r);for(let o of r.versions||[])o.htmlRel&&await Rn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Rn(r,o.v,"png"),o.videoRel&&await Rn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Ch,VT,zT,EL,YT,WT,_L,ta,IL,JT,na,$L,qT,RL,OL,XT,Mt,jo,Rh,$n,Rn,Yi,CL,FL,Oh,ra=q(()=>{fe();gr();$h();br();Ch=TL(SL),VT=process.env.BOSTRAT_STATE_DIR||Ze.join(process.env.HOME||KT.tmpdir(),".bostrat"),zT=Ze.join(VT,"mocks.json"),EL=process.env.BOSTRAT_MOCK_ENGINE||"chromium",YT=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",WT=process.env.BOSTRAT_FIREFOX_BIN||"firefox",_L=1300,ta=2,IL=32,JT=1500,na=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,$L=700*1024*1024,qT=100*1024*1024,RL=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",OL=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",XT=2e4,Mt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
202
|
+
`)}catch{return""}}async function pF(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await uF(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await sA(e.slug)||"server exited before listening"),sr.delete(e.id),await ar(e,e.state,e.error?{error:e.error}:{}),!0}if(await nc(e.port))return sr.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await ar(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>dF?(e.state="error",e.error=await sA(e.slug)||"never started listening",await ar(e,"error",{error:e.error}),!0):!1;let o=(sr.get(e.id)||0)+1;return sr.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Qu(){if(!$h){$h=!0;try{let e=await ir(),t=!1;for(let n of e.views)await pF(n)&&(t=!0);t&&await Cn()}finally{$h=!1}}}async function Bh({trackId:e}={}){await Qu();let t=await ir();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function mA(e){let n=(await ir()).views.find(r=>r.id===e);return n?{...n}:null}function Lh(e){return Rn&&Rn.views.find(t=>t.slug===e)||null}async function Fh({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=It.resolve(r,o??"."),d=i==="detected"?null:async()=>{await aF(r,u),await iF(u,n)},p=await ir();if(i!=="detected"){let m=Ch(t),g=String(n).trim(),b=p.views.find(x=>x.trackId===e&&(x.slug===m||Ch(x.name)===m||x.cmd===g&&It.resolve(x.serveCwd??x.cwd)===u));if(b)return fF(p,b,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(m=>m.state==="live"||m.state==="starting").length>=Xr)throw new Error(`view cap reached (${Xr} live) \u2014 stop one first`);let w={id:qL().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:cF(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await lF(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await jh(w),p.views.push(w),await Cn(),await ar(w,"created",{origin:i}),Oh(e,w.id),i==="detected"&&await nc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Cn(),await ar(w,"live")),{...w}}async function fF(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 oF(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await nc(t.port))return await Cn(),Oh(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>=Xr)throw new Error(`view cap reached (${Xr} live) \u2014 stop one first`);return t.origin==="detected"?(await Dh(t.port),t.origin="agent"):await rc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),sr.delete(t.id),await jh(t),await Cn(),await ar(t,"created",{restarted:!0,reused:!0}),Oh(t.trackId,t.id),{...t,reused:!0}}async function hA(e){let n=(await ir()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Dh(n.port):await rc(n.slug),n.state!=="stopped"&&(n.state="stopped",sr.delete(n.id),await Cn(),await ar(n,"stopped")),{...n}}async function wA(e){let t=await ir(),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>=Xr)throw new Error(`view cap reached (${Xr} live) \u2014 stop one first`);return await rc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),sr.delete(n.id),await jh(n),await Cn(),await ar(n,"created",{restarted:!0}),{...n}}async function gA(e){let t=await ir(),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 Dh(r.port):await rc(r.slug),t.views.splice(n,1),sr.delete(r.id),await Cn(),await ar(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function yA(e){let t=await ir(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await rc(r.slug),sr.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Cn()),n.length}async function bA(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await ir(),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 Cn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(mF)){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(!fA().has(c)&&!n.views.some(l=>l.port===c)&&await nc(c))try{i.push(await Fh({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 On,zL,aA,nA,rA,Xr,VL,sa,oA,Qi,JL,Rh,YL,XL,ZL,Mh,iA,_h,tF,rF,lA,Xu,Rn,Ih,ec,Nh,uA,sr,dF,$h,mF,ed=q(()=>{ct();de();vr();On=GL(KL),zL=process.env.BOSTRAT_STATE_DIR||It.join(process.env.HOME||HL.tmpdir(),".bostrat"),aA=It.join(zL,"views.json"),[nA,rA]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Xr=Number(process.env.BOSTRAT_VIEW_MAX)||3,VL="bostrat-view-",sa=e=>`${VL}${e}`,oA=new Set(["bostrat","www","api","views","app"]),Qi=e=>Object.assign(new Error(e),{status:400}),JL=new Set(["npm","pnpm","yarn","bun"]),Rh="package.json",YL=/[|;&`]|\$\(/,XL=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),ZL=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Mh=e=>String(e).trim().split(/\s+/).filter(Boolean),iA=/^[A-Za-z_][A-Za-z0-9_]*=/;_h=e=>e==="/",tF=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");rF=e=>tc.stat(e).then(t=>t.isDirectory(),()=>!1),lA=e=>tc.stat(e).then(t=>t.isFile(),()=>!1),Xu=e=>tc.realpath(e).catch(()=>It.resolve(e));Rn=null,Ih=null;ec=new Map,Nh=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,uA=12e3;sr=new Map,dF=12e4;$h=!1;mF=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function td(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>wF?null:n}function gF(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function nd(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=td(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await Lo(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(Uh.get(e.id)||0)<hF)return;Uh.set(e.id,a);let i;try{i=await En(e.cwd,{caller:"reconcile"})}catch{return}let c=await Qs(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await _e(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Ct({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!p&&!(d?.trackId===e.id&&await Yn(u,l.number))){if(!gF(d,o))return;let f=await K(e).catch(()=>null);if(f&&td(f)==null)return;await C(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:l.number,url:l.url,title:l.title,branch:n,repo:u}});return}}await C(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),Uh.delete(e.id)}var hF,Uh,wF,rd=q(()=>{de();Bo();kr();Zn();Fi();Fo();hF=6e3,Uh=new Map,wF=30*6e4});import Hh from"node:os";import{execFile as yF}from"node:child_process";import{promisify as bF}from"node:util";async function od(){if(process.platform!=="darwin")return Hh.freemem();try{let{stdout:e}=await kF("vm_stat",[],{timeout:3e3}),t=Number(/page size of (\d+) bytes/.exec(e)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(e)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*t;return r>0?r:Hh.freemem()}catch{return Hh.freemem()}}var kF,Wh=q(()=>{kF=bF(yF)});import Ye from"node:fs/promises";import Ze from"node:path";import SA from"node:os";import{randomUUID as xF}from"node:crypto";import{execFile as vF}from"node:child_process";import{promisify as SF}from"node:util";import{chromium as TF}from"playwright-core";async function Pn(){return Wo||(qh||=(async()=>{try{Wo=JSON.parse(await Ye.readFile(AA,"utf8"))}catch{Wo={mocks:[]}}finally{qh=null}return Wo.mocks||=[],Wo})(),qh)}async function Gt(){await Et(AA,Wo)}function $A(e,t){Nn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Nn(e,t.v,"png")}function kt(e,t,n={}){return C({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 zt(e,t){let n=sc(e.cwd);return{html:Ze.join(n,`${e.id}.v${t}.html`),png:Ze.join(n,`${e.id}.v${t}.png`)}}function oc(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Ze.join(e.cwd,r):null}function an(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function zh(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function sd(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(OF)}function CF(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 MF(e){let t=Buffer.alloc(24),n=await Ye.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function NF({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 PF(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,m)=>{r.onload=w,r.onerror=()=>m(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,p=-1,f=-1;for(let w=0;w<s;w++)for(let m=w*o*4,g=0;g<o;g++,m+=4)Math.abs(c[m]-l[0])+Math.abs(c[m+1]-l[1])+Math.abs(c[m+2]-l[2])>24&&(g<u&&(u=g),g>p&&(p=g),w<d&&(d=w),f=w);return p<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:p,bottom:f}}},t.toString("base64"))}async function DF(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,p)=>{c.onload=d,c.onerror=()=>p(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 jF({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await TF.launch({executablePath:EA,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Mt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:aa})).newPage();a.setDefaultTimeout(ia),await a.goto(`file://${e}`,{waitUntil:"load",timeout:ia}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:ia});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await PF(a,i),u=l&&NF({...l,pad:_F*aa,align:aa});u&&(i=await DF(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<_A)throw new Mt(`capture produced a ${i.length}-byte image`);let c=zh(i);if(!c)throw new Mt("output is not a PNG");return await Ye.writeFile(t,i),{width:Math.round(c.width/aa),height:Math.round(c.height/aa),dpr:aa,renderMs:Date.now()-r}}catch(s){throw s instanceof Mt?s:new Mt(/timeout/i.test(s?.message||"")?`render timed out after ${ia}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function vA({htmlPath:e,outPath:t,width:n}){let r=await Ye.mkdtemp(Ze.join(SA.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Gh(kA,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:ia,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Mt(`firefox not found (${kA})`,""):new Mt(c?.killed?`render timed out after ${ia}ms`:"firefox exited non-zero",s)}let a=await Ye.stat(t).catch(()=>null);if(!a||a.size<_A)throw new Mt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=zh(await MF(t));if(!i)throw new Mt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Ye.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function BF(e){if(AF==="firefox")return vA(e);try{return await jF(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await vA(e)}catch(n){throw/not found/.test(n?.message||"")?new Mt(`no browser installed \u2014 chromium missing at ${EA} 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 RA(e){return Kh=Kh.then(e,e),Kh}async function OA(e,t){let n=await Pn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Gt(),await kt(r,"rendering",{version:t});try{let o=await od();if(o<IF)throw new Mt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=zt(r,t),a=await LF({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Ye.rm(s.png,{force:!0}).catch(()=>{});return}Vh(r,{v:t,source:"html",htmlRel:Ze.relative(r.cwd,s.html),pngRel:Ze.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Gt(),await kt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Mn(r),Nn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Gt(),await kt(r,"error",{version:t,error:r.error}),Mn(r)}}}function Vh(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 FF({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?EF:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function ac({trackId:e,group:t}={}){let n=await Pn(),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 cr(e){let n=(await Pn()).mocks.find(r=>r.id===e);return n?{...n}:null}function CA({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&&!sd(t))throw new Error("image must be a PNG");if(n){let o=CF(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>xA)throw new Error(`video must be ${xA/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function MA({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=FF({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=CA({html:n,image:r,video:o}),w=await Pn(),m={id:xF().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:p?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Ye.mkdir(sc(s),{recursive:!0}),w.mocks.push(m),p?(await Ye.writeFile(zt(m,1).html,n),await Gt(),await kt(m,"created"),Mn(m),Nn(m,1,"html"),RA(()=>OA(m.id,1))):o?(await PA(m,1,o,f),await Gt(),await kt(m,"created"),await kt(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr,durationS:m.versions[0]?.durationS??null}),Mn(m),$A(m,m.versions[0])):(await DA(m,1,r),await Gt(),await kt(m,"created"),await kt(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),Mn(m),Nn(m,1,"png")),{...m}}async function NA(e,{html:t,image:n,video:r,width:o}={}){let a=(await Pn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=CA({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await Ye.mkdir(sc(a.cwd),{recursive:!0}),i?(a.state="queued",await Ye.writeFile(zt(a,l).html,t),await Gt(),await kt(a,"updated",{version:l}),Mn(a),Nn(a,l,"html"),RA(()=>OA(a.id,l))):r?(await PA(a,l,r,c),await Gt(),await kt(a,"updated",{version:l}),await kt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:an(a)?.durationS??null}),Mn(a),$A(a,a.versions.find(u=>u.v===l))):(await DA(a,l,n),await Gt(),await kt(a,"updated",{version:l}),await kt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Mn(a),Nn(a,l,"png")),{...a}}async function UF(e){try{let{stdout:t}=await Gh($F,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:IA,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 HF(e,t){try{await Gh(RF,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:IA,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Ye.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function PA(e,t,n,r){let o=sc(e.cwd),s=Ze.join(o,`${e.id}.v${t}.${r.ext}`),a=Ze.join(o,`${e.id}.v${t}.poster.png`);await Ye.writeFile(s,n);let i=await UF(s),c=await HF(s,a);Vh(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Ze.relative(e.cwd,s),posterRel:c?Ze.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function DA(e,t,n){let r=zh(n);if(!r)throw new Error("image must be a PNG");let o=zt(e,t);await Ye.writeFile(o.png,n),Vh(e,{v:t,source:"image",htmlRel:null,pngRel:Ze.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function jA(e){let t=await Pn(),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 kt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function BA(e){let t=await Pn(),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=zt(r,o);await Ye.rm(s.html,{force:!0}).catch(()=>{}),await Ye.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Ye.rm(Ze.join(r.cwd,s),{force:!0}).catch(()=>{});return await Gt(),await kt(r,"deleted"),Zt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function LA(e){let n=(await Pn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Ze.join(TA,"mock-archive",e.id),o=sc(e.cwd),s=Ze.join(r,".bostrat","mocks");await Ye.mkdir(s,{recursive:!0});let a=await Ye.readdir(o).catch(()=>[]);for(let i of a)await Ye.copyFile(Ze.join(o,i),Ze.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Ye.stat(Ze.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Mn(i)}return await Gt(),n.length}async function FA(e){let t=await Pn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Gt();for(let r of n)Zt("mocks",r.id).catch(()=>{})}return n.length}async function UA(){if(!Fe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await st("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Pn();for(let r of n.mocks){await Mn(r);for(let o of r.versions||[])o.htmlRel&&await Nn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Nn(r,o.v,"png"),o.videoRel&&await Nn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Gh,TA,AA,AF,EA,kA,EF,aa,_F,_A,ia,IF,xA,$F,RF,IA,Mt,Wo,qh,Mn,Nn,sc,OF,LF,Kh,ca=q(()=>{de();vr();Wh();Tr();Gh=SF(vF),TA=process.env.BOSTRAT_STATE_DIR||Ze.join(process.env.HOME||SA.tmpdir(),".bostrat"),AA=Ze.join(TA,"mocks.json"),AF=process.env.BOSTRAT_MOCK_ENGINE||"chromium",EA=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",kA=process.env.BOSTRAT_FIREFOX_BIN||"firefox",EF=1300,aa=2,_F=32,_A=1500,ia=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,IF=700*1024*1024,xA=100*1024*1024,$F=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",RF=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",IA=2e4,Mt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
201
203
|
${n.trim().split(`
|
|
202
204
|
`).slice(-6).join(`
|
|
203
|
-
`)}`:t),this.name="MockRenderError"}},jo=null,Rh=null;$n=e=>yt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Rn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?zi(e,r,"video"):r&&zi(e,r,n)||(n==="html"?Vt(e,t).html:null);return o?yr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Yi=e=>Ze.join(e,".bostrat","mocks");CL=Buffer.from([137,80,78,71,13,10,26,10]);FL=LL,Oh=Promise.resolve()});import qL from"node:fs/promises";import hA from"node:path";import GL from"node:os";import{randomUUID as wA}from"node:crypto";async function ar(){return sr||(Ph||=(async()=>{let e=!0;try{sr=JSON.parse(await qL.readFile(gA,"utf8"))}catch{sr={flows:[]},e=!1}finally{Ph=null}if(sr.flows||=[],!e&&Le()){let{records:t,source:n}=await ot("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&sr.flows.push(r.data);sr.flows.length&&await oa().catch(()=>{})}}return sr})(),Ph)}async function oa(){await Et(gA,sr)}function Dh(e,t,n={}){return C({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 yA({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>dA)throw new Error(`steps must be an array of 1-${dA}`);let r=await Ji({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>fA)throw new Error(`${l}: caption over ${fA} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>mA)throw new Error(`${l}: title over ${mA} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(x=>x.id===p);if(f.length||(f=r.filter(x=>x.name.toLowerCase()===p.toLowerCase())),!f.length)throw new Error(`${l}: no mock matching "${p}" in this Track \u2014 see \`bostrat-mock ls\``);if(f.length>1)throw new Error(`${l}: "${p}" is ambiguous (${f.map(x=>`mock:${x.id}`).join(", ")}) \u2014 use the id`);let h=f[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(x=>x.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let g=o.findIndex(x=>x.mockId===h.id),k=g>=0?o.splice(g,1)[0].id:wA().slice(0,8);s.push({id:k,mockId:h.id,caption:u,title:d,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function VL(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>pA)throw new Error(`name over ${pA} chars`);return e.trim()}async function bA({trackId:e}={}){let t=await ar();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function zL(e){let n=(await ar()).flows.find(r=>r.id===e);return n?{...n}:null}async function kA(e){let t=await zL(e);if(!t)return null;let n=new Map((await Ji({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=cn(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 xA({trackId:e,name:t,steps:n,cwd:r,box:o,agent:s}){if(!e)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=VL(t),i=await ar(),c=i.flows.find(f=>f.trackId===e&&f.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 yA({trackId:e,steps:n}),d=Date.now(),p={id:wA().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(p),await oa(),await Dh(p,"created"),Xu(p),{...p}}async function vA(e,{steps:t,note:n}={}){let o=(await ar()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await yA({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 oa(),await Dh(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Xu(o),{...o}}async function SA(e){let t=await ar(),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 oa(),await Dh(r,"deleted"),Qt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function TA(e){return(await ar()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function AA(e){let n=(await ar()).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(),Xu(r);return n.length&&await oa(),n.length}async function EA(e){let t=await ar(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await oa();for(let r of n)Qt("flows",r.id).catch(()=>{})}return n.length}async function _A(){if(!Le())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await ar();for(let r of n.flows)await Xu(r);return{pushed:n.flows.length,source:"plane"}}var KL,gA,dA,pA,fA,mA,sr,Ph,Xu,jh=q(()=>{fe();gr();ra();br();KL=process.env.BOSTRAT_STATE_DIR||hA.join(process.env.HOME||GL.tmpdir(),".bostrat"),gA=hA.join(KL,"flows.json"),dA=20,pA=80,fA=500,mA=80,sr=null,Ph=null;Xu=e=>yt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import lr from"node:fs/promises";import ur from"node:path";import YL from"node:os";import{randomUUID as JL}from"node:crypto";function Xi(e){return XL.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Cn(){return ir||(Bh||=(async()=>{let e=!0;try{ir=JSON.parse(await lr.readFile($A,"utf8"))}catch{ir={runbooks:[]},e=!1}finally{Bh=null}return ir.runbooks||=[],!e&&Le()&&await ZL().catch(()=>{}),ir})(),Bh)}async function Lo(){await Et($A,ir)}function Qu(e){return Zi(yt("runbooks",{...e},{trackId:e.trackId}))}async function ZL(){let{records:e,source:t}=await ot("runbooks");if(!(t!=="plane"||!e.length)){await lr.mkdir(Zu,{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 Va("runbooks",r.id,o.v,"md");typeof s=="string"&&await lr.writeFile(cr(r,o.v),s).catch(()=>{})}ir.runbooks.push(r)}ir.runbooks.length&&await Lo()}}function sa(e,t,n={}){return C({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 cr(e,t){return ur.join(Zu,`${e.id}.v${t}.md`)}function RA(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 OA(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>IA)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${IA/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Xi(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 QL(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function eF({trackId:e,status:t}={}){let n=await Cn(),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 CA({trackId:e,status:t}={}){let n=await Cn(),r=await eF({trackId:e,status:t});if(!Le())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await ot("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Lh(e){let t=await tF(e);if(t)return t;if(!Le())return null;let{records:n,source:r}=await ot("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function MA(e,t){let n=await Va("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function tF(e){let n=(await Cn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function NA({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=OA(r),c=await Cn(),l={id:JL().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 lr.mkdir(Zu,{recursive:!0}),await lr.writeFile(cr(l,1),r),l.versions.push({v:1,mdRel:ur.relative(Bo,cr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Lo(),await sa(l,"created",{version:1}),Qu(l),Zi(yr("runbooks",l.id,1,"md",cr(l,1))),{...l}}async function PA(e,{md:t,note:n}={}){let o=(await Cn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");QL(o,"update");let s=OA(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await lr.mkdir(Zu,{recursive:!0}),await lr.writeFile(cr(o,a),t),o.versions.push({v:a,mdRel:ur.relative(Bo,cr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Lo(),await sa(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Qu(o),Zi(yr("runbooks",o.id,a,"md",cr(o,a))),{...o}}async function DA(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Cn()).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 Lo(),await sa(r,t==="done"?"done":"reopened"),Qu(r),{...r})}async function jA(e){let t=await Cn(),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 lr.rm(ur.join(Bo,o.mdRel),{force:!0}).catch(()=>{});return await Lo(),r.trackDeletedAt||await sa(r,"deleted"),Zi(Qt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function BA(e){let n=(await Cn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await sa(r,"orphaned"),r.cwd=null,Qu(r);return n.length&&await Lo(),n.length}async function LA(e){let t=await Cn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await sa(r,"deleted");for(let o of r.versions||[])await lr.rm(ur.join(Bo,o.mdRel),{force:!0}).catch(()=>{});Zi(Qt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Lo()),n.length}async function FA(){if(!Le())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ot("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Cn();for(let r of n.runbooks){await yt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await yr("runbooks",r.id,o.v,"md",ur.join(Bo,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Bo,$A,Zu,IA,XL,ir,Bh,Zi,ed=q(()=>{gr();fe();br();Bo=process.env.BOSTRAT_STATE_DIR||ur.join(process.env.HOME||YL.tmpdir(),".bostrat"),$A=ur.join(Bo,"runbooks.json"),Zu=ur.join(Bo,"runbooks"),IA=256*1024,XL=[{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/}];ir=null,Bh=null;Zi=e=>Promise.resolve(e).catch(()=>{})});import Fh from"node:fs/promises";import Qi from"node:path";import{randomUUID as nF}from"node:crypto";function Hh(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return rF.has(t)?"image":oF.has(t)?"doc":null}function sF(e){let r=Qi.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 WA(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>UA)throw new Error(`file too large \u2014 max ${Math.floor(UA/(1024*1024))} MB`);let r=sF(t),o=Qi.extname(r).slice(1),s=Hh(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=nF().slice(0,8),i=Uh(e);await Fh.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Fh.writeFile(Qi.join(i,c),n,{mode:384}),await Fh.chmod(Qi.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${HA}/${c}`,kind:s,size:n.length}}function qA(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Wh(e){return e.role==="user"&&aF.test((e.text||"").trim())}var HA,Uh,UA,rF,oF,aF,qh=q(()=>{HA=".bostrat/uploads",Uh=e=>Qi.join(e,HA),UA=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,rF=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),oF=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"]);aF=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Fo(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var GA=q(()=>{});import{execFile as iF}from"node:child_process";import{promisify as cF}from"node:util";function Gh(e){return`refs/bostrat/wip/${e}`}function dF(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function zA(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function pF(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:uF}async function Kh(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await Kr(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Kr(o,["remote","get-url","origin"])}catch{return KA.has(o)||(KA.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 Bs(o,"bostrat: wip snapshot"),{stdout:a}=await Kr(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&td.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=Gh(r);return await Kr(o,["push","--force","origin",`${s}:${c}`],VA),td.set(o,i),await C(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:c,sha:s,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${r}: pushed ${s.slice(0,7)} \u2192 ${c}`),{pushed:!0,sha:s,ref:c}}catch(s){return t.log?.(`[wip-sync] ${r}: push failed (will retry): ${String(s?.message||s).split(`
|
|
204
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
205
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
205
|
+
`)}`:t),this.name="MockRenderError"}},Wo=null,qh=null;Mn=e=>yt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Nn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?oc(e,r,"video"):r&&oc(e,r,n)||(n==="html"?zt(e,t).html:null);return o?Sr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};sc=e=>Ze.join(e,".bostrat","mocks");OF=Buffer.from([137,80,78,71,13,10,26,10]);LF=BF,Kh=Promise.resolve()});import WF from"node:fs/promises";import GA from"node:path";import qF from"node:os";import{randomUUID as zA}from"node:crypto";async function ur(){return lr||(Jh||=(async()=>{let e=!0;try{lr=JSON.parse(await WF.readFile(VA,"utf8"))}catch{lr={flows:[]},e=!1}finally{Jh=null}if(lr.flows||=[],!e&&Fe()){let{records:t,source:n}=await st("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&lr.flows.push(r.data);lr.flows.length&&await la().catch(()=>{})}}return lr})(),Jh)}async function la(){await Et(VA,lr)}function Yh(e,t,n={}){return C({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 JA({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>HA)throw new Error(`steps must be an array of 1-${HA}`);let r=await ac({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>qA)throw new Error(`${l}: caption over ${qA} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>KA)throw new Error(`${l}: title over ${KA} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(x=>x.id===p);if(f.length||(f=r.filter(x=>x.name.toLowerCase()===p.toLowerCase())),!f.length)throw new Error(`${l}: no mock matching "${p}" in this Track \u2014 see \`bostrat-mock ls\``);if(f.length>1)throw new Error(`${l}: "${p}" is ambiguous (${f.map(x=>`mock:${x.id}`).join(", ")}) \u2014 use the id`);let w=f[0],m=null;if(c.v!=null&&(m=Number(c.v),!Number.isInteger(m)||!(w.versions||[]).some(x=>x.v===m)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let g=o.findIndex(x=>x.mockId===w.id),b=g>=0?o.splice(g,1)[0].id:zA().slice(0,8);s.push({id:b,mockId:w.id,caption:u,title:d,v:m})}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 GF(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>WA)throw new Error(`name over ${WA} chars`);return e.trim()}async function YA({trackId:e}={}){let t=await ur();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function zF(e){let n=(await ur()).flows.find(r=>r.id===e);return n?{...n}:null}async function XA(e){let t=await zF(e);if(!t)return null;let n=new Map((await ac({trackId:t.trackId})).map(r=>[r.id,r]));return t.steps=t.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=an(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),t}async function ZA({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=GF(t),i=await ur(),c=i.flows.find(f=>f.trackId===e&&f.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 JA({trackId:e,steps:n}),d=Date.now(),p={id:zA().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(p),await la(),await Yh(p,"created"),ad(p),{...p}}async function QA(e,{steps:t,note:n}={}){let o=(await ur()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await JA({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 la(),await Yh(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),ad(o),{...o}}async function eE(e){let t=await ur(),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 la(),await Yh(r,"deleted"),Zt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function tE(e){return(await ur()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function nE(e){let n=(await ur()).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(),ad(r);return n.length&&await la(),n.length}async function rE(e){let t=await ur(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await la();for(let r of n)Zt("flows",r.id).catch(()=>{})}return n.length}async function oE(){if(!Fe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await st("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await ur();for(let r of n.flows)await ad(r);return{pushed:n.flows.length,source:"plane"}}var KF,VA,HA,WA,qA,KA,lr,Jh,ad,Xh=q(()=>{de();vr();ca();Tr();KF=process.env.BOSTRAT_STATE_DIR||GA.join(process.env.HOME||qF.tmpdir(),".bostrat"),VA=GA.join(KF,"flows.json"),HA=20,WA=80,qA=500,KA=80,lr=null,Jh=null;ad=e=>yt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import fr from"node:fs/promises";import mr from"node:path";import VF from"node:os";import{randomUUID as JF}from"node:crypto";function ic(e){return YF.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Dn(){return dr||(Zh||=(async()=>{let e=!0;try{dr=JSON.parse(await fr.readFile(aE,"utf8"))}catch{dr={runbooks:[]},e=!1}finally{Zh=null}return dr.runbooks||=[],!e&&Fe()&&await XF().catch(()=>{}),dr})(),Zh)}async function Ko(){await Et(aE,dr)}function cd(e){return cc(yt("runbooks",{...e},{trackId:e.trackId}))}async function XF(){let{records:e,source:t}=await st("runbooks");if(!(t!=="plane"||!e.length)){await fr.mkdir(id,{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 Za("runbooks",r.id,o.v,"md");typeof s=="string"&&await fr.writeFile(pr(r,o.v),s).catch(()=>{})}dr.runbooks.push(r)}dr.runbooks.length&&await Ko()}}function ua(e,t,n={}){return C({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 pr(e,t){return mr.join(id,`${e.id}.v${t}.md`)}function iE(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 cE(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>sE)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${sE/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=ic(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 ZF(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function QF({trackId:e,status:t}={}){let n=await Dn(),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 lE({trackId:e,status:t}={}){let n=await Dn(),r=await QF({trackId:e,status:t});if(!Fe())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await st("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 Qh(e){let t=await eU(e);if(t)return t;if(!Fe())return null;let{records:n,source:r}=await st("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 uE(e,t){let n=await Za("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function eU(e){let n=(await Dn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function dE({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=cE(r),c=await Dn(),l={id:JF().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 fr.mkdir(id,{recursive:!0}),await fr.writeFile(pr(l,1),r),l.versions.push({v:1,mdRel:mr.relative(qo,pr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Ko(),await ua(l,"created",{version:1}),cd(l),cc(Sr("runbooks",l.id,1,"md",pr(l,1))),{...l}}async function pE(e,{md:t,note:n}={}){let o=(await Dn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");ZF(o,"update");let s=cE(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await fr.mkdir(id,{recursive:!0}),await fr.writeFile(pr(o,a),t),o.versions.push({v:a,mdRel:mr.relative(qo,pr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Ko(),await ua(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),cd(o),cc(Sr("runbooks",o.id,a,"md",pr(o,a))),{...o}}async function fE(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Dn()).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 Ko(),await ua(r,t==="done"?"done":"reopened"),cd(r),{...r})}async function mE(e){let t=await Dn(),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 fr.rm(mr.join(qo,o.mdRel),{force:!0}).catch(()=>{});return await Ko(),r.trackDeletedAt||await ua(r,"deleted"),cc(Zt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function hE(e){let n=(await Dn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await ua(r,"orphaned"),r.cwd=null,cd(r);return n.length&&await Ko(),n.length}async function wE(e){let t=await Dn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await ua(r,"deleted");for(let o of r.versions||[])await fr.rm(mr.join(qo,o.mdRel),{force:!0}).catch(()=>{});cc(Zt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Ko()),n.length}async function gE(){if(!Fe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await st("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Dn();for(let r of n.runbooks){await yt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Sr("runbooks",r.id,o.v,"md",mr.join(qo,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var qo,aE,id,sE,YF,dr,Zh,cc,ld=q(()=>{vr();de();Tr();qo=process.env.BOSTRAT_STATE_DIR||mr.join(process.env.HOME||VF.tmpdir(),".bostrat"),aE=mr.join(qo,"runbooks.json"),id=mr.join(qo,"runbooks"),sE=256*1024,YF=[{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/}];dr=null,Zh=null;cc=e=>Promise.resolve(e).catch(()=>{})});import ew from"node:fs/promises";import lc from"node:path";import{randomUUID as tU}from"node:crypto";function nw(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return nU.has(t)?"image":rU.has(t)?"doc":null}function oU(e){let r=lc.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 kE(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>yE)throw new Error(`file too large \u2014 max ${Math.floor(yE/(1024*1024))} MB`);let r=oU(t),o=lc.extname(r).slice(1),s=nw(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=tU().slice(0,8),i=tw(e);await ew.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await ew.writeFile(lc.join(i,c),n,{mode:384}),await ew.chmod(lc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${bE}/${c}`,kind:s,size:n.length}}function xE(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function rw(e){return e.role==="user"&&sU.test((e.text||"").trim())}var bE,tw,yE,nU,rU,sU,ow=q(()=>{bE=".bostrat/uploads",tw=e=>lc.join(e,bE),yE=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,nU=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),rU=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"]);sU=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Go(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var vE=q(()=>{});import{execFile as aU}from"node:child_process";import{promisify as iU}from"node:util";function sw(e){return`refs/bostrat/wip/${e}`}function uU(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function AE(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function dU(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:lU}async function aw(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await Zr(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Zr(o,["remote","get-url","origin"])}catch{return SE.has(o)||(SE.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 Hs(o,"bostrat: wip snapshot"),{stdout:a}=await Zr(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&ud.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=sw(r);return await Zr(o,["push","--force","origin",`${s}:${c}`],TE),ud.set(o,i),await C(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(`
|
|
206
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function iw(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Zr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Zr(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await Hs(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await Zr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(ud.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=uU(n);return await Zr(r,["push","origin",`${o}:${i}`],TE),ud.set(r,a),await C(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(`
|
|
207
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function EE({listTracks:e,sweepMs:t=dU(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)Ve(i)||i?.kind==="setup"||await aw(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 cU,lU,TE,Zr,ud,SE,cw=q(()=>{uu();de();An();cU=iU(aU),lU=12e4,TE=6e4;Zr=(e,t,n=15e3)=>cU("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),ud=new Map,SE=new Set});import pU from"node:fs";import lw from"node:fs/promises";import fU from"node:path";function gU(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function _E(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(!gU(t))return{restored:!1,reason:"plane not configured"};let s=gs(e),a=fU.join(s,`${o}.ndjson`);try{if(pU.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 p=new AbortController,f=setTimeout(()=>p.abort(),wU),w;try{w=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${mU}`,{headers:c,signal:p.signal})}finally{clearTimeout(f)}if(w.status===404)return{restored:!1,reason:"unknown to plane"};if(!w.ok)return{restored:!1,reason:`plane ${w.status}`};let m=await w.json();for(let g of m.events||[])l.push(JSON.stringify(g));if(l.length>hU)return{restored:!1,reason:"event count over safety cap"};if(m.next==null)break;u=m.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await lw.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await lw.writeFile(d,l.join(`
|
|
206
208
|
`)+`
|
|
207
|
-
`),await
|
|
209
|
+
`),await lw.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var mU,hU,wU,IE=q(()=>{pt();de();mU=1e3,hU=5e5,wU=3e4});function $E(e,t=Date.now()){let n=e.find(s=>s.reconcile||s.ciFix);if(n?.reconcile)return{status:"working",since:n.reconcile.delegatedAt,summary:"reconciling the branch"};if(n?.ciFix)return{status:"working",since:n.ciFix.delegatedAt,summary:"repairing failed checks"};let r=e.find(s=>s.reconcileAwait||s.ciFixAwait);if(r?.reconcileAwait)return{status:"waiting",since:r.reconcileAwait.since,summary:"fix pushed \u2014 waiting for mergeability"};if(r?.ciFixAwait)return{status:"waiting",since:r.ciFixAwait.since,summary:"fix pushed \u2014 waiting for CI"};let o=e.filter(s=>s.autoMerge||s.armedAt);return o.length?{status:"waiting",since:Math.min(...o.map(s=>s.autoMerge?.armedAt||s.armedAt||t)),count:o.length,summary:o.length===1?"auto-merge armed \u2014 lands when checks pass":`auto-merge armed on ${o.length} PRs`}:null}function RE(e){let t=(e||[]).filter(n=>!yU.has(n.state)&&!n.ciFix&&!n.reconcile&&!n.ciFixAwait&&!n.reconcileAwait&&!n.autoMerge&&!n.armedAt);return t.length?{count:t.length,since:Math.min(...t.map(n=>n.announcedAt||n.firstSeen||Date.now())),summary:t.length===1?`PR #${t[0].number} is open \u2014 review, merge or arm it`:`${t.length} open PRs waiting on you`}:null}function OE({archivedAt:e=null,archiving:t=!1,prompted:n=!1,openActions:r=0,openActionSince:o=null,loopAttention:s=!1,prDecisions:a=null,operation:i=null,phase:c=null,working:l=!1,workingSince:u=null,pending:d=null,completedTurnAt:p=null,live:f=!1,now:w=Date.now()}={}){return e&&!t?cn("archived","lifecycle",e):n||r>0||s?cn("needs-you",n?"pane":s?"loop":"obligations",o||w,{count:r||(n||s?1:null)}):t?cn("archiving","lifecycle",e||w):a?.count?cn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="working"?cn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?cn("working","agent-phase",c.at,{summary:c.summary??null}):l?cn("working","pane",u||w):i?.status==="waiting"?cn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?cn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?cn("settled","obligations",p,{summary:"no open actions"}):cn("idle","fallback",p||w)}var cn,yU,CE=q(()=>{cn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});yU=new Set(["merged","closed","lost"])});async function ME(e){if(dd.has(e.id))return dd.get(e.id);let t=await K(e).catch(()=>[]),n=[];try{n=Sn(t)}catch{}let r=Zl(n),o=n.filter(f=>f?.kind==="followup-offer"&&f.status==="open"),s=new Set(t.filter(f=>f.type==="prompt.answered").map(f=>f.payload?.promptId)),a=t.filter(f=>f.type==="prompt.raised"&&f.payload?.promptId&&!s.has(f.payload.promptId)),i=[...r,...o,...a.map(f=>({ts:f.payload?.ts??f.ts}))],c=null;for(let f of t){if(f.type!=="track.phase.changed")continue;let w=f.payload||{};c=w.phase?{phase:w.phase,summary:w.summary??null,scope:w.scope??null,phaseId:w.phaseId??null,at:w.ts??f.ts}:null}let l=n.flatMap(f=>f?.kind==="job-dock"?f.jobs||[]:[]).filter(f=>f?.status==="running"),u=t.reduce((f,w)=>w.type==="turn.agent"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,d=t.reduce((f,w)=>w.type==="turn.user"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,p={openActions:i.length,openActionSince:i.length?Math.min(...i.map(f=>f.ts||Date.now())):null,phase:c,completedTurnAt:u,currentTurnAt:d,runningJobs:l.length,runningJobsSince:l.length?Math.min(...l.map(f=>f.startedTs||Date.now())):null};return dd.set(e.id,p),p}var dd,NE=q(()=>{de();Ql();dd=new Map;ot.on("event",e=>{e?.trackId&&dd.delete(e.trackId)})});import{randomUUID as bU}from"node:crypto";async function uw(e,t,n={}){let r={operationId:bU(),kind:t,startedAt:Date.now(),...n};return pd.set(e.id,r),await C(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:r.startedAt,state:"started",...r}}).catch(()=>{}),r}async function dw(e,t,n="completed",r=null){pd.get(e.id)?.operationId===t?.operationId&&pd.delete(e.id),await C(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:Date.now(),state:"finished",operationId:t?.operationId,kind:t?.kind,outcome:n,...r?{error:String(r)}:{}}}).catch(()=>{})}var pd,PE,pw=q(()=>{de();pd=new Map;PE=e=>pd.get(e)||null});var f_={};Ap(f_,{HANDOFF_LATEST_REL:()=>zo,KNOWN_AGENTS:()=>bc,TRACK_NAME_MAX:()=>Aw,addTrackRepo:()=>Hw,adoptTrack:()=>ag,agentAvailability:()=>gd,announceNodeUpdate:()=>Bn,answerFullscreenOffer:()=>Qw,answerPrompt:()=>bd,archiveTrack:()=>Yo,archiveTrackForMergedPr:()=>nr,augmentFirstPromptAfterHandoff:()=>KE,buildAgentCmd:()=>Iw,canResumeSubtask:()=>u_,cancelQueued:()=>vc,cancelQueuedToDraft:()=>Pw,cancelStopJob:()=>Sc,cancelSubtask:()=>$d,changedRepos:()=>Ho,commitTransferOut:()=>_d,createSetupTrack:()=>Kw,createTrack:()=>vd,deleteTrack:()=>zw,demoteTransferredTracks:()=>sg,ensureLeadEpochs:()=>ga,forceMergeSubtask:()=>ug,getHandoffJob:()=>xa,getSubtaskJob:()=>A4,getTrack:()=>oe,getTransferOutJob:()=>ng,healDeadLead:()=>gc,interruptLead:()=>xd,isArchiving:()=>f4,isHandoffNoteEcho:()=>xw,isInterruptEcho:()=>vw,isModelSwitchEcho:()=>Sw,leadCliDead:()=>Jw,listSubtaskJobs:()=>Zo,listTracks:()=>me,matchTranscriptsToBoxes:()=>VE,mergeNotifyMessage:()=>a_,navigatePrompt:()=>Mw,notifyTrackForMergedPr:()=>rr,notifyTrackForResolvedPr:()=>Sd,offerFollowupsForMergedPr:()=>Xo,pendingOffers:()=>Ow,queueGauges:()=>Nw,queueShipCompleteNotify:()=>jw,queuedMessages:()=>Vo,reapArchivedOrphans:()=>Zw,reconcileDemotedTracks:()=>Xw,reconcileHandoffOutcome:()=>i_,reconcileInterruptedHandoffs:()=>tg,reconcileInterruptedSubtasks:()=>ig,reconcileNodeUpdateNotices:()=>Rw,reconcileSubtaskOutcome:()=>l_,refreshStandingInstructions:()=>qw,refuseIfTransferred:()=>og,rehydrateLead:()=>Ac,removeTrackRepo:()=>Ww,renameTrack:()=>Yw,reposOf:()=>jt,resolveTrackBox:()=>Vi,resolveTrackBoxByHookToken:()=>xc,restartLead:()=>Ad,resumeAgentCmd:()=>$w,sayToLead:()=>Dt,sendOptionsForAgent:()=>vn,setTrackDraft:()=>Fw,setTrackModel:()=>Uw,startLeadHandoff:()=>Ed,startSpinUpView:()=>Bw,startSubtask:()=>lg,startTransferOut:()=>rg,stopJobActive:()=>XE,subtaskMessages:()=>Cw,trackMessages:()=>yd,unarchiveTrack:()=>Td});import Q from"node:fs/promises";import re from"node:path";import{randomUUID as hd}from"node:crypto";import{execFile as kU}from"node:child_process";import{promisify as xU}from"node:util";function Iw(e,{skipPermissions:t=!0}={}){let n=wd[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 $w(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=wd[e];if(!s)throw new Error(`unknown agent "${e}"`);let a=process.env[s.envVar];if(a)return a;let i=n&&s.skipFlag?` ${s.skipFlag}`:"";switch(e){case"claude":{if(!t)return`${s.base}${i}`;if(r&&!tn(r,t)){let c=o&&Be(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 ka(e){let t=wd[e];if(t)return process.env[t.envVar]||t.base}async function gd({maxAgeMs:e=6e4}={}){if(Qr.value&&Date.now()-Qr.at<e)return Qr.value;let t=Qr.value,n=await Promise.all(bc.map(async r=>{let o=await Cl(ka(r),{retries:[]});return o===Ss?[r,t?.[r]??!0]:[r,o===Ol]}));return Qr.value=Object.fromEntries(n),Qr.at=Date.now(),Qr.value}async function kc(e,t=!0){let n=Iw(e,{skipPermissions:t});if(await Cl(n)===vs){Qr.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.":tf[r]?`Install it on this node: ${tf[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 KE(e,t=zo){return`${e.trim()}
|
|
208
210
|
|
|
209
|
-
(Read ${t} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function
|
|
210
|
-
`;return ew=ew.then(()=>Q.mkdir(Mn,{recursive:!0})).then(()=>Q.appendFile(vF,n)).catch(()=>{}),ew}async function Nn({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await Q.readFile(tw,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await ne(),u=0;for(let d of l.tracks)Ve(d)||!d.leadBox||(Ie(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ce().catch(()=>{}),s==="available")try{await Q.mkdir(te.dirname(tw),{recursive:!0}),await Q.writeFile(tw,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function ww({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await ne(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||IF(e,c)||(Ie(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ce().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function IF(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 fE(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=>$F.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 mE(e,t,n,{kind:r}={}){let o=await ve(t);if(!o)throw new Error(`box "${t}" is not running`);let s=ma(o.agent);await Tr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await nt(t,n,{...kn(o.agent,n),kind:r||"orchestration"});let a=await mi({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await ue({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await ne(),c=ke(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);Ie(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await ce()}async function RF(e){let t=te.join(ps(e),`${e.id}.ndjson`),n;try{n=await Q.stat(t)}catch{return null}let r=rE.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await G(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return rE.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function OF(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Dm(n.name,n.agent)}async function Nt(e,t=null,n=null){let r=t??new Map((await en()).map(y=>[y.name,y])),o=!!e.transferredTo,s=e.boxes.map(y=>{let I=o?null:r.get(y.name),R=zr.has(y.state)?y.state:I?y.role==="lead"?"live":y.state:"stopped";return{...y,state:R,cwd:I?.cwd||y.cwd,agent:I?.agent||y.agent,sessionId:I?.sessionId||y.sessionId}}),[a,{working:i,prompted:c},l,u]=await Promise.all([RF(e),OF(e,s),eE(e),n??Vn().catch(()=>[])]),d=ic.has(e.id),p=(u||[]).filter(y=>y.trackId===e.id&&!["merged","closed","lost"].includes(y.state)),f=p.find(y=>y.reconcile||y.ciFix),h=nE(e.id)||(f?.reconcile?{kind:"reconcile",startedAt:f.reconcile.delegatedAt}:f?.ciFix?{kind:"fix-ci",startedAt:f.ciFix.delegatedAt}:null),w=p.filter(y=>y.ciFixAwait||y.reconcileAwait||y.autoMerge),g=w.length?{since:Math.min(...w.map(y=>y.ciFixAwait?.since||y.reconcileAwait?.since||y.armedAt||Date.now())),count:w.length,summary:w.some(y=>y.ciFixAwait)?"waiting for CI":w.some(y=>y.reconcileAwait)?"waiting for mergeability":"auto-merge armed"}:null,k=s.find(y=>y.name===e.leadBox)?.state==="live",x=ZA({archivedAt:e.archivedAt,archiving:d,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,operation:h,phase:l.phase,working:i,workingSince:l.currentTurnAt,waiting:g,completedTurnAt:l.completedTurnAt||a,live:k});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:d,status:x}}function hE(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-CF,p=r[u+1]?.createdAt??1/0,f=o.find(h=>!s.has(h.id)&&h.birthMs>=d&&h.birthMs<p);f&&(s.add(f.id),c.push({box:l.name,sessionId:f.id}))}return c}async function wE(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(p=>p.box.sessionId),a=tk(r),i=new Set(a.map(p=>p.id)),c=s.every(p=>p&&i.has(p)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(p=>p.box),d=hE(u,a,p=>i.has(p));for(let p of d){let f=u.find(h=>h.name===p.box);f&&(f.sessionId=p.sessionId),await Kb(p.box,p.sessionId),n=!0}}return n}async function pe(){let e=await ne();await wE(e)&&await ce();let t=new Map((await en()).map(r=>[r.name,r])),n=await Vn().catch(()=>[]);return Promise.all(e.tracks.map(r=>Nt(r,t,n)))}async function Hi(e,t){let n=await ne();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function pc(e){let t=Na(e);if(!t)return null;let n=await ne();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Ny(o.hookTokenHash,t))return{track:r,box:o};return null}async function oe(e){let t=await ne(),n=ke(t,e);return n?(await wE(t)&&await ce(),Nt(n)):null}async function gw(e){let t=await G(e).catch(()=>[]),n;try{n=xn(t)}catch{return[]}return Wl(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function cw(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(MF[e.agent]||[]).some(n=>n.test(t))}function lw(e){let t=(e.text||"").trim();return t?NF.some(n=>n.test(t)):!1}async function cd(e,t={}){let n=await oe(e);if(!n)return null;let r=new Set([...n.events.filter(_=>_.type==="orchestration-prompt"&&_.prompt).map(_=>String(_.prompt).trim()),...n.events.filter(_=>_.type==="said"&&_.text).map(_=>String(_.text).trim())]),o=ha(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=vh(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...PT(n),...l],p={windows:[...N0(n),...P0(n,Vl),...a,...u]},h=(await Promise.all(n.boxes.map(_=>Yf(_,t)))).flat(),w=h.map(_=>{let F={kind:"message",..._};if(F.role==="assistant"&&(F.text||"").trim()&&F.tools?.length){let ge=xi(F.agent).cliTools,Ne=F.tools.filter(Be=>!ge.has(Be));return{...F,tools:Ne.length?Ne:void 0}}return F}).map(_=>{if(_.role!=="user")return _;let F=Rs(_.text);return F?F.kind==="suppress"?null:{..._,note:F,text:r0(F)}:_}).filter(Boolean).filter(_=>!(_.role==="user"&&r.has((_.text||"").trim()))).filter(_=>!cw(_)).filter(_=>!lw(_)).filter(_=>!iw(_)).filter(_=>!Wh(_)).filter(_=>_.role!=="assistant"||!!(_.text||"").trim()||!!_.images?.length).filter(_=>!D0(_,p)),g=w.filter(_=>_.failed&&_.text===ho);if(g.length){let _=new Map;for(let F of g){if(!_.has(F.box)){let Be=[...(await De(F.box,{lines:tt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(St=>St[1]);_.set(F.box,Be.length?Be[Be.length-1]:null)}let ge=_.get(F.box);ge&&(F.text=`\u25A0 ${ge}`)}}let k=await Promise.all(n.boxes.map(_=>hx(_,t)));await gT(n,k.flat());let{agentTurnBoxes:x}=await hT(n,w);for(let _ of n.boxes)x.has(_.name)&&_.cwd&&dl(_.cwd);await uv(n),await HT(n,w).catch(()=>{});for(let _ of n.boxes)_.agent==="codex"&&_.state==="live"&&await bT(n,_),_.agent==="cursor"&&_.state==="live"&&await TT(n,_);let y=await Promise.all(n.boxes.map(_=>mx(_,t)));await wT(n,y.flat().filter(_=>!nw(_.box,_.ts,u)));let I=await G(n),R=c&&i.viewId?i.viewId:null,P=vv(I),H=xn(I).filter(_=>!(_.kind==="message"&&(cw(_)||lw(_)||iw(_)||Wh(_)))&&!(_.kind==="view-card"&&R&&_.viewId===R)),j=Sv(H,P),A=H.filter(_=>!(_.kind==="message"&&_.role==="assistant"&&!j.has(_)&&nw(_.box,_.ts,P))),O=Rf(I,A).map(_=>({kind:"command-batch",id:_.id,ts:_.pending?_.ts:(_.anchorTs??_.ts)-1,anchorTs:_.anchorTs,anchorBox:_.anchorBox,agent:_.agent,boxRole:_.boxRole,commands:_.commands,pending:_.pending})).filter(_=>!nw(_.anchorBox,_.anchorTs??_.ts,u)),N=u0(d0([...A,...O].filter(_=>_.ts!=null).sort((_,F)=>_.ts-F.ts))),z=DF(n),$=z.size?N.filter(_=>_.kind==="event"||!z.has(_.box??_.anchorBox)):N,T=n.boxes.find(_=>_.name===n.leadBox)||null,M=T?await ri(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!M&&T){let _=[...I].reverse().find(F=>F.type==="context.usage"&&F.boxId===T.name);_?.payload?.window&&(M={pct:_.payload.pct??(_.payload.occupancy!=null?_.payload.occupancy/_.payload.window:null),occupancy:_.payload.occupancy??null,window:_.payload.window,windowKnown:!0,model:_.payload.model||null})}let W=[...h].reverse().find(_=>_.role==="assistant"&&_.model),K=W?.model||null,be=[...n.events||[]].reverse().find(_=>_.type==="model-change"),se=M?.model||K;be&&(!W?.ts||be.at>=W.ts)&&(se=be.to);let we=T?await De(T.name,{lines:tt}).catch(()=>""):"",ae=T&&To(T.agent,we)?await Hn(T.name).catch(()=>we):we,V=T?Cr(T,ae):null;if(V&&T){let _=Zl(T,V,ae);if(_){let F=Ox(T.name,V.promptId);F.action!=="give-up"&&(F.action==="send"&&(qe(T.name,_.keys,{kind:"auto-reject"}).catch(()=>{}),F.first&&C(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{ts:Date.now(),promptId:V.promptId,rule:_.id}}).catch(()=>{})),V=null)}}!V&&T&&!_s(T.agent,we)&&!Ho(n.id).length&&(V=If($,{leadBoxId:T.name,provider:T.agent,isAnswered:_=>I.some(F=>F.type==="prompt.answered"&&F.payload?.promptId===_)})),V&&dv(n,T,V);let de=$.filter(_=>!(_.kind==="message"&&_.role==="assistant"&&_.ask&&!String(_.text||"").trim()&&!_.images?.length)),Y=!!T&&Tu(T.name,T.agent,we)&&!V;T&&!T.readyAt&&(Y||V||we&&Is(T.agent,we))&&(T.readyAt=Date.now(),await ce());let $e=!!T&&!T.readyAt&&Date.now()-(n.createdAt||0)<PF;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&Is(T.agent,we)&&!V){let _=await ne(),F=ke(_,n.id);F?.setup&&!F.setup.connectedAt&&(F.setup.connectedAt=Date.now(),n.setup.connectedAt=F.setup.connectedAt,await C(F,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:F.setup.connectedAt}}).catch(()=>{}),await ce())}let Pe=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,me=null;if(c){let _=Date.now()-i.startedAt;if(i.viewId&&!Y){let F=Hu(n.id);F&&(Ie(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:F.finishedAt}),await ce())}else if(V||_>MT&&!Y&&!i.viewId||_>xh){let F=V?"needs-input":_>xh?"timeout":"no-view";Hu(n.id,{error:F})&&await C(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:F}}).catch(()=>{})}else me={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:de,context:M?{pct:M.pct,occupancy:M.occupancy,window:M.window,windowKnown:M.windowKnown,model:M.model}:null,models:{current:se,options:rv(T?.agent)},pendingPrompt:V,working:Y,stopping:yE(n.id),leadStarting:$e,handoff:Pe,view:me,queued:Ho(n.id).filter(_=>_.kind!=="ship-notify"&&_.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function DF(e){let t=new Set;for(let n of e.boxes||[])n.role==="subtask"&&n.name&&t.add(n.name);for(let n of Object.values(e.subtasks||{}))n?.box&&t.add(n.box);return t}async function yw(e,t){let n=await oe(e);if(!n)return null;let r=Vo(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 G(n),a=xn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Rf(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,p)=>d.ts-p.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await De(o,{lines:tt}).catch(()=>"");u=Tu(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function ld(e,{promptId:t,answers:n,text:r}={}){let o=ke(await ne(),e);if(!o)throw new Error(`track "${e}" not found`);mc(e,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await ve(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await nm(s);if(i||(i=await jF(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let h=typeof r=="string"?r.trim():"";if(!h)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(h)||h.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await nt(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await qe(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=i0(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Dt(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=_x(i,n||[]);await qe(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=$k(i,n||[]);await nt(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let g=h[w],k=new Set(Array.isArray(n?.[w])?n[w]:[]),x=[...k].map(I=>g.options?.[I]).filter(Boolean).map(I=>({label:I.label,description:I.description||""})),y=(g.options||[]).map((I,R)=>k.has(R)?null:I?.label).filter(Boolean);bm(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:x,rejected:y})}return await ue({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),d=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),p=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),f=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:p,...f?{control:f}:{}},await ue({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),bm(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function jF(e,t){if(!t)return null;let n=await G(e).catch(()=>[]);return If(xn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function bw(e,{promptId:t,questionIndex:n}={}){let r=ke(await ne(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await ve(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await nm(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=Ix(a,n);return c.length?(await qe(r.leadBox,c,{kind:"navigate-prompt"}),await ue({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function ud(e){let t=rc.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},rc.set(e,t)),t}function Ho(e){return(rc.get(e)?.items??[]).map(t=>({...t}))}function fc(e){let t=rc.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 kw(e){let t=fc(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=ke(await ne(),e);if(o)return r=ua(o,n),await ce(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function xw(e){let t=ud(e);t.timer||(t.timer=setInterval(()=>LF(e).catch(()=>{}),BF))}async function LF(e){let t=rc.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=ke(await ne(),e);if(!n||n.archivedAt){fc(e);return}let r=ha(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Fo(t,!1);return}let o=await ve(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await Nw(o)&&(o=await cc(n,s,"queue")?await ve(n.leadBox):null,await ce()),!o){Fo(t,!1);return}if(Eo(s?.agent)){Fo(t,!1);return}let a=await De(n.leadBox,{lines:tt}).catch(()=>"");if(Nl(s?.agent,a)){Fo(t,!1);return}if(To(s?.agent,a)){let c=await Hn(n.leadBox).catch(()=>a);if(Cr(s,c)){Fo(t,!1);return}}if(!Fo(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await uw(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 cc(n,s,"queue-gate")}await ce()}finally{t.flushing=!1}}}async function gE(e,t,n,r){let o=Eo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ua(e,[n]),await ce()),Vx(e.id,o))try{await C(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 FF(e,t){let n=await De(e.leadBox,{lines:tt}).catch(()=>"");if(!To(t?.agent,n))return!1;let r=await Hn(e.leadBox).catch(()=>n);return!!Cr(t,r)}async function uw(e,t,n,r,o=null,s=null){await gE(e,t,r,o);let a=oc.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=pE(r,e.pendingHandoffHydrate.artifact||Uo),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(p=>p&&p.rel):[];if(u.length){let p=qA(u.map(f=>f.rel));i=i.trim()?`${i.trim()}
|
|
211
|
+
(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 xw(e){return e.role==="user"&&TU.test((e.text||"").trim())}async function ee(){return Vt||(fw||=(async()=>{try{Vt=JSON.parse(await Q.readFile(WE,"utf8"))}catch{Vt={tracks:[]}}finally{fw=null}return Vt})(),fw)}async function ae(){await Et(WE,Vt)}function _U(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
212
|
+
`;return mw=mw.then(()=>Q.mkdir(jn,{recursive:!0})).then(()=>Q.appendFile(vU,n)).catch(()=>{}),mw}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 Q.readFile(hw,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await ee(),u=0;for(let d of l.tracks)Ve(d)||!d.leadBox||(Ie(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await Q.mkdir(re.dirname(hw),{recursive:!0}),await Q.writeFile(hw,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Rw({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await ee(),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)||IU(e,c)||(Ie(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function IU(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 GE(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=>$U.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 zE(e,t,n,{kind:r}={}){let o=await ke(t);if(!o)throw new Error(`box "${t}" is not running`);let s=ka(o.agent),a=Yi({text:n,producer:Ji({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=we(await ee(),e)||null,c=Date.now(),l=null;try{l=await $r({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(g){throw Zi(a,{ms:Date.now()-c,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),await $n(i,a,oa(g)),g}Zi(a,{ms:Date.now()-c,outcome:l||"up"});let u=vn(o.agent,n);a.paste=!!u.paste;try{await nt(t,n,{...u,kind:r||"orchestration"})}catch(g){throw await $n(i,a,oa(g)),g}let d={},p=await ki({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await $n(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await le({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await ee(),w=we(f,e);if(!w)return;let m=w.boxes.find(g=>g.name===t);Ie(w,"orchestration-prompt",{box:t,agent:m?.agent||o.agent,boxRole:m?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function RU(e){let t=re.join(gs(e),`${e.id}.ndjson`),n;try{n=await Q.stat(t)}catch{return null}let r=DE.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await K(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return DE.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function OU(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Gm(n.name,n.agent)}async function Nt(e,t=null,n=null){let r=t??new Map((await Qt()).map(y=>[y.name,y])),o=!!e.transferredTo,s=e.boxes.map(y=>{let $=o?null:r.get(y.name),O=eo.has(y.state)?y.state:$?y.role==="lead"?"live":y.state:"stopped";return{...y,state:O,cwd:$?.cwd||y.cwd,agent:$?.agent||y.agent,sessionId:$?.sessionId||y.sessionId}}),[a,{working:i,prompted:c},l,u]=await Promise.all([RU(e),OU(e,s),ME(e),n??Xn().catch(()=>[])]),d=wc.has(e.id),p=(u||[]).filter(y=>y.trackId===e.id&&!["merged","closed","lost"].includes(y.state)),f=PE(e.id),w=f&&{status:"waiting",since:f.startedAt,summary:f.summary??null,count:f.count??null}||$E(p),m=s.find(y=>y.name===e.leadBox)?.state==="live",g=m?Vo(e.id).filter(y=>y.kind!=="ship-notify"&&y.kind!=="prompt-answer"):[],b=m&&l.runningJobs?{source:"jobs",count:l.runningJobs,since:l.runningJobsSince,summary:l.runningJobs===1?"a background job is still running":`${l.runningJobs} background jobs are still running`}:g.length?{source:"queue",count:g.length,since:Math.min(...g.map(y=>y.ts||Date.now())),summary:"a queued message is about to send"}:null,x=OE({archivedAt:e.archivedAt,archiving:d,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:RE(p),operation:w,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:b,completedTurnAt:l.completedTurnAt||a,live:m});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:d,status:x}}function VE(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-CU,p=r[u+1]?.createdAt??1/0,f=o.find(w=>!s.has(w.id)&&w.birthMs>=d&&w.birthMs<p);f&&(s.add(f.id),c.push({box:l.name,sessionId:f.id}))}return c}async function JE(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(p=>p.box.sessionId),a=kk(r),i=new Set(a.map(p=>p.id)),c=s.every(p=>p&&i.has(p)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(p=>p.box),d=VE(u,a,p=>i.has(p));for(let p of d){let f=u.find(w=>w.name===p.box);f&&(f.sessionId=p.sessionId),await uk(p.box,p.sessionId),n=!0}}return n}async function me(){let e=await ee();await JE(e)&&await ae();let t=new Map((await Qt()).map(r=>[r.name,r])),n=await Xn().catch(()=>[]);return Promise.all(e.tracks.map(r=>Nt(r,t,n)))}async function Vi(e,t){let n=await ee();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 xc(e){let t=La(e);if(!t)return null;let n=await ee();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Zy(o.hookTokenHash,t))return{track:r,box:o};return null}async function oe(e){let t=await ee(),n=we(t,e);return n?(await JE(t)&&await ae(),Nt(n)):null}async function Ow(e){let t=await K(e).catch(()=>[]),n;try{n=Sn(t)}catch{return[]}return Zl(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function vw(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(MU[e.agent]||[]).some(n=>n.test(t))}function Sw(e){let t=(e.text||"").trim();return t?NU.some(n=>n.test(t)):!1}async function yd(e,t={}){let n=await oe(e);if(!n)return null;let r=new Set([...n.events.filter(I=>I.type==="orchestration-prompt"&&I.prompt).map(I=>String(I.prompt).trim()),...n.events.filter(I=>I.type==="said"&&I.text).map(I=>String(I.text).trim())]),o=xa(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=Ph(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...pA(n),...l],p={windows:[...ex(n),...tx(n,nu),...a,...u]},w=(await Promise.all(n.boxes.map(I=>om(I,t)))).flat(),m=w.map(I=>{let j={kind:"message",...I};if(j.role==="assistant"&&(j.text||"").trim()&&j.tools?.length){let fe=jr(j.agent).cliTools,Pe=j.tools.filter(Ce=>!fe.has(Ce));return{...j,tools:Pe.length?Pe:void 0}}return j}).map(I=>{if(I.role!=="user")return I;let j=v0(I);return j?j.kind==="suppress"?null:{...I,note:j,text:S0(j)}:I}).filter(Boolean).filter(I=>!(I.role==="user"&&r.has((I.text||"").trim()))).filter(I=>!vw(I)).filter(I=>!Sw(I)).filter(I=>!xw(I)).filter(I=>!rw(I)).filter(I=>I.role!=="assistant"||!!(I.text||"").trim()||!!I.images?.length).filter(I=>!nx(I,p)),g=m.filter(I=>I.failed&&I.text===xo);if(g.length){let I=new Map;for(let j of g){if(!I.has(j.box)){let Ce=[...(await je(j.box,{lines:tt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(wn=>wn[1]);I.set(j.box,Ce.length?Ce[Ce.length-1]:null)}let fe=I.get(j.box);fe&&(j.text=`\u25A0 ${fe}`)}}let b=await Promise.all(n.boxes.map(I=>Bx(I,t)));await FT(n,b.flat());let{agentTurnBoxes:x}=await BT(n,m);for(let I of n.boxes)x.has(I.name)&&I.cwd&&kl(I.cwd);await Mv(n),await bA(n,m).catch(()=>{});for(let I of n.boxes)I.agent==="codex"&&I.state==="live"&&await HT(n,I),I.agent==="cursor"&&I.state==="live"&&await zT(n,I);let y=await Promise.all(n.boxes.map(I=>jx(I,t)));await LT(n,y.flat().filter(I=>!ww(I.box,I.ts,u)));let $=await K(n),O=c&&i.viewId?i.viewId:null,M=Kv($),H=Sn($).filter(I=>!(I.kind==="message"&&(vw(I)||Sw(I)||xw(I)||rw(I)))&&!(I.kind==="view-card"&&O&&I.viewId===O)),P=Gv(H,M),E=H.filter(I=>!(I.kind==="message"&&I.role==="assistant"&&!P.has(I)&&ww(I.box,I.ts,M))),R=Ff($,E).map(I=>({kind:"command-batch",id:I.id,ts:I.pending?I.ts:(I.anchorTs??I.ts)-1,anchorTs:I.anchorTs,anchorBox:I.anchorBox,agent:I.agent,boxRole:I.boxRole,commands:I.commands,pending:I.pending})).filter(I=>!ww(I.anchorBox,I.anchorTs??I.ts,u)),G=R0(O0([...E,...R].filter(I=>I.ts!=null).sort((I,j)=>I.ts-j.ts))),V=DU(n),A=V.size?G.filter(I=>I.kind==="event"||!V.has(I.box??I.anchorBox)):G,_=n.boxes.find(I=>I.name===n.leadBox)||null,N=_?await ci(_.cwd,_.sessionId,{fallbackNewest:!1}):null;if(!N&&_){let I=[...$].reverse().find(j=>j.type==="context.usage"&&j.boxId===_.name);I?.payload?.window&&(N={pct:I.payload.pct??(I.payload.occupancy!=null?I.payload.occupancy/I.payload.window:null),occupancy:I.payload.occupancy??null,window:I.payload.window,windowKnown:!0,model:I.payload.model||null})}let U=[...w].reverse().find(I=>I.role==="assistant"&&I.model),z=U?.model||null,ue=[...n.events||[]].reverse().find(I=>I.type==="model-change"),pe=N?.model||z;ue&&(!U?.ts||ue.at>=U.ts)&&(pe=ue.to);let Se=_?await je(_.name,{lines:tt}).catch(()=>""):"",Ae=_&&Ro(_.agent,Se)?await Gn(_.name).catch(()=>Se):Se,W=_?Br(_,Ae):null;if(W&&_){let I=cu(_,W,Ae);if(I){let j=ev(_.name,W.promptId);j.action!=="give-up"&&(j.action==="send"&&(Ke(_.name,I.keys,{kind:"auto-reject"}).catch(()=>{}),j.first&&C(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:_.name,provider:_.agent,payload:{ts:Date.now(),promptId:W.promptId,rule:I.id}}).catch(()=>{})),W=null)}}!W&&_&&!Cs(_.agent,Se)&&!Vo(n.id).length&&(W=Bf(A,{leadBoxId:_.name,provider:_.agent,isAnswered:I=>$.some(j=>j.type==="prompt.answered"&&j.payload?.promptId===I)})),W&&Nv(n,_,W);let te=A.filter(I=>!(I.kind==="message"&&I.role==="assistant"&&I.ask&&!String(I.text||"").trim()&&!I.images?.length)),J=!!_&&Nu(_.name,_.agent,Se)&&!W;_&&!_.readyAt&&(J||W||Se&&Ms(_.agent,Se))&&(_.readyAt=Date.now(),await ae());let ge=!!_&&!_.readyAt&&Date.now()-(n.createdAt||0)<PU;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&_&&Ms(_.agent,Se)&&!W){let I=await ee(),j=we(I,n.id);j?.setup&&!j.setup.connectedAt&&(j.setup.connectedAt=Date.now(),n.setup.connectedAt=j.setup.connectedAt,await C(j,{type:"provider.connected",actor:"system",source:"pane",boxId:_.name,provider:_.agent,payload:{provider:_.agent,ts:j.setup.connectedAt}}).catch(()=>{}),await ae())}let Oe=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Me=null;if(c){let I=Date.now()-i.startedAt;if(i.viewId&&!J){let j=Zu(n.id);j&&(Ie(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:j.finishedAt}),await ae())}else if(W||I>uA&&!J&&!i.viewId||I>Nh){let j=W?"needs-input":I>Nh?"timeout":"no-view";Zu(n.id,{error:j})&&await C(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:_?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:j}}).catch(()=>{})}else Me={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:te,context:N?{pct:N.pct,occupancy:N.occupancy,window:N.window,windowKnown:N.windowKnown,model:N.model}:null,models:{current:pe,options:Ev(_?.agent)},pendingPrompt:W,working:J,stopping:XE(n.id),leadStarting:ge,handoff:Oe,view:Me,queued:Vo(n.id).filter(I=>I.kind!=="ship-notify"&&I.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function DU(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 Cw(e,t){let n=await oe(e);if(!n)return null;let r=Zo(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 K(n),a=Sn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Ff(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,p)=>d.ts-p.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await je(o,{lines:tt}).catch(()=>"");u=Nu(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function bd(e,{promptId:t,answers:n,text:r}={}){let o=we(await ee(),e);if(!o)throw new Error(`track "${e}" not found`);Sc(e,"prompt-answer");let s=o.boxes.find(w=>w.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await ke(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await pm(s);if(i||(i=await jU(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 w=typeof r=="string"?r.trim():"";if(!w)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(w)||w.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await nt(o.leadBox,w,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Ke(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let w=_0(i,n||[],typeof r=="string"?r:"");if(!w)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Dt(o.id,w,{kind:"prompt-answer"}),c={mode:"say",value:w,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let w=Yx(i,n||[]);await Ke(o.leadBox,w,{kind:"answer-prompt"}),c={mode:"keys",value:w.join(" "),count:w.length}}else{let w=Vk(i,n||[]);await nt(o.leadBox,w,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:w,count:1}}if(i.answerMode==="say"){let w=i.questions||[];for(let m=0;m<w.length;m++){let g=w[m],b=new Set(Array.isArray(n?.[m])?n[m]:[]),x=[...b].map($=>g.options?.[$]).filter(Boolean).map($=>({label:$.label,description:$.description||""})),y=(g.options||[]).map(($,O)=>b.has(O)?null:$?.label).filter(Boolean);$m(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:m,questionTotal:i.questionTotal??w.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:x,rejected:y})}return await le({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(w=>l.options?.[w]).filter(Boolean).map(w=>({label:w.label,description:w.description||""})),p=(l.options||[]).map((w,m)=>u.has(m)?null:w?.label).filter(Boolean),f=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:p,...f?{control:f}:{}},await le({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),$m(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function jU(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return Bf(Sn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Mw(e,{promptId:t,questionIndex:n}={}){let r=we(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await ke(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await pm(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=Xx(a,n);return c.length?(await Ke(r.leadBox,c,{kind:"navigate-prompt"}),await le({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 kd(e){let t=ha.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},ha.set(e,t)),t}function Vo(e){return(ha.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function Nw(e=Date.now()){let t=0,n=0;for(let r of ha.values())for(let o of r.items){t+=1;let s=e-(o.delivery?.queuedAt??o.ts??e);s>n&&(n=s)}return{queueDepth:t,queueOldestAgeMs:n}}function LU(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=we(await ee(),e)||null;for(let s of r)await $n(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function vc(e,t="cancel"){let n=ha.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),LU(e,r,t),r}async function Pw(e){let t=vc(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=we(await ee(),e);if(o)return r=wa(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Dw(e){let t=kd(e);t.timer||(t.timer=setInterval(()=>FU(e).catch(()=>{}),BU))}async function FU(e){let t=ha.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=we(await ee(),e);if(!n||n.archivedAt){vc(e,"archived");return}let r=l=>{for(let u of t.items)tA(u.delivery,l)},o=xa(e);if(o&&!o.finishedAt&&o.step!=="done"&&!o.error){Go(t,!1),r("handoff");return}let s=await ke(n.leadBox),a=n.boxes.find(l=>l.name===n.leadBox);if(s&&await Jw(s)&&(s=await gc(n,a,"queue")?await ke(n.leadBox):null,await ae()),!s){Go(t,!1),r("noSession");return}if(Co(a?.agent)){Go(t,!1),r("cap");return}let i=await je(n.leadBox,{lines:tt}).catch(()=>"");if(ql(a?.agent,i)){Go(t,!1),r("busy");return}if(Ro(a?.agent,i)){let l=await Gn(n.leadBox).catch(()=>i);if(Br(a,l)){Go(t,!1),r("prompt");return}}if(!Go(t,!0)){r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await Tw(n,a,s,c.text,c.kind??null,c.attachments??null,c.delivery??null)}catch(l){if(l?.code!=="cli-not-running")throw c.delivery&&await $n(n,c.delivery,oa(l)),l;t.items.unshift(c),c.delivery&&(c.delivery.requeued+=1,c.delivery.dequeuedAt=null,await Xi(n,c.delivery,{position:1,reason:"requeue"})),await gc(n,a,"queue-gate")}await ae()}finally{t.flushing=!1}}}async function YE(e,t,n,r){let o=Co(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(wa(e,[n]),await ae()),wv(e.id,o))try{await C(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 UU(e,t){let n=await je(e.leadBox,{lines:tt}).catch(()=>"");if(!Ro(t?.agent,n))return!1;let r=await Gn(e.leadBox).catch(()=>n);return!!Br(t,r)}async function Tw(e,t,n,r,o=null,s=null,a=null){a||(a=Yi({text:r,producer:Ji({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await YE(e,t,r,o);let i=fc.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=KE(r,e.pendingHandoffHydrate.artifact||zo),delete e.pendingHandoffHydrate,l=!0);let d=Array.isArray(s)?s.filter(m=>m&&m.rel):[];if(d.length){let m=xE(d.map(g=>g.rel));c=c.trim()?`${c.trim()}
|
|
211
213
|
|
|
212
|
-
${
|
|
214
|
+
${m}`:m}let p=Ie(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||ma(e,null);try{let m=Date.now(),g=null;try{g=await $r({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:ka(n.agent),bailOnDialog:!0})}catch(x){throw Zi(a,{ms:Date.now()-m,outcome:x?.code==="cli-not-running"?"cli-not-running":"blocked"}),x}if(Zi(a,{ms:Date.now()-m,outcome:g||"up"}),(fc.get(e.id)||0)!==i){!o&&r.trim()&&(wa(e,[r]),await ae());let x=new Error("delivery stopped \u2014 the message was handed back as the draft");throw x.interrupted=!0,x}if(await UU(e,t)){let x=kd(e.id);a.requeued+=1,a.dequeuedAt=null;let y={id:a.id,text:r,ts:Date.now(),...o?{kind:o}:{},...d.length?{attachments:d}:{},delivery:a};x.items.unshift(y),Dw(e.id),await Xi(e,a,{position:1,reason:"prompt"});let $=new Error("lead is blocked on a prompt \u2014 message queued");throw $.requeued={id:y.id,position:x.items.length},$}let b=vn(t?.agent,c);a.paste=!!b.paste,await nt(e.leadBox,c,{...b,kind:l?"lead-handoff-first-prompt":"say"})}catch(m){if(u&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=u),Ie(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,...m?.interrupted?{reason:"interrupted"}:{},...m?.requeued?{reason:"requeued"}:{}}),m?.requeued)return{queued:!0,...m.requeued};throw m?.code!=="cli-not-running"&&await $n(e,a,oa(m)),m}let f={},w=await n4(e,t,c,f);a.verify=f,w?await $n(e,a,f.verdict==="unverified"?"unverified":"delivered"):(await ZE(e.leadBox,t?.agent,c.length+8,c).catch(()=>{}),Ie(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,reason:"unsubmitted"}),o||wa(e,[r]),await $n(e,a,"unsubmitted")),l&&await le({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function jw(e,{actions:t}={}){let n=kd(e),r=zv(t),o=Yi({text:r,producer:Ji({kind:"ship-notify"}),kind:"ship-notify"});n.items.push({id:o.id,text:r,ts:Date.now(),kind:"ship-notify",delivery:o}),Dw(e),(async()=>{let s=we(await ee(),e);s&&(o.provider=s.boxes.find(a=>a.name===s.leadBox)?.agent??null,await Xi(s,o,{position:n.items.length,reason:"queue"}))})().catch(()=>{})}async function Dt(e,t,{kind:n=null,attachments:r=null,producer:o=null}={}){let s=Array.isArray(r)?r.filter(u=>u&&typeof u.rel=="string"):[];if((typeof t!="string"||!Fs(t))&&!s.length)throw new Error("text (string) or an attachment required");let a=we(await ee(),e);if(!a)throw new Error(`track "${e}" not found`);if(a.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);og(a);let i=a.boxes.find(u=>u.name===a.leadBox),c=Fs(t),l=Yi({text:c,producer:Ji({kind:n,producer:o}),kind:n,provider:i?.agent??null,attachments:s.length});try{return await HU(e,a,i,c,{kind:n,atts:s,d:l})}catch(u){throw await $n(a,l,oa(u)),u}}async function HU(e,t,n,r,{kind:o,atts:s,d:a}){await YE(t,n,r,o),Sc(e,"say");let i=await ke(t.leadBox);if(i&&await Jw(i)?await gc(t,n,"say")?(await ae(),i=await ke(t.leadBox)):i=null:!i&&await Ac(t)&&(await ae(),i=await ke(t.leadBox)),!i)throw new Error(`lead box "${t.leadBox}" is not running`);let c=kd(e),l=await je(t.leadBox,{lines:tt}).catch(()=>""),u=c.items.length>0?"queue":ql(n?.agent,l)?"busy":null,d=u!=null;if(!d&&Ro(n?.agent,l)){let f=await Gn(t.leadBox).catch(()=>l);d=!!Br(n,f),d&&(u="prompt")}if(d){let f={id:a.id,text:r,ts:Date.now(),...o?{kind:o}:{},...s.length?{attachments:s}:{},delivery:a};return c.items.push(f),Dw(e),o||ma(t,null),await ae(),await Xi(t,a,{position:c.items.length,reason:u}),{ok:!0,leadBox:t.leadBox,queued:!0,id:f.id,position:c.items.length,deliveryId:a.id}}let p;try{p=await Tw(t,n,i,r,o,s,a)}catch(f){if(f?.code!=="cli-not-running"||!await gc(t,n,"say-gate")||(await ae(),i=await ke(t.leadBox),!i))throw f;p=await Tw(t,n,i,r,o,s,a)}return await ae(),p?.queued?{ok:!0,leadBox:t.leadBox,queued:!0,id:p.id,position:p.position,deliveryId:a.id}:{ok:!0,leadBox:t.leadBox,deliveryId:a.id}}async function Bw(e,{hint:t}={}){let n=we(await ee(),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=Ph(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";dA(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${jE}
|
|
213
215
|
|
|
214
|
-
What to preview: ${String(t).trim()}`:
|
|
216
|
+
What to preview: ${String(t).trim()}`:jE;try{await Dt(e,a,{kind:"spin-up-view"})}catch(i){throw Zu(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function xd(e){let t=we(await ee(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);fc.set(e,(fc.get(e)||0)+1);let n=vc(e,"interrupt"),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=fa.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=wa(t,r),await ae()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=VU(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Jo(WU)]);let a=we(await ee(),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 XE(e){let t=fa.get(e);return!!t&&!t.canceled}function Sc(e,t){let n=fa.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function Lw(e){Sc(e,"track-ended"),fc.delete(e),vc(e,"track-ended")}function VU(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}),fa.set(e,r),(async()=>{let s=we(await ee(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await ke(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<qU&&!r.canceled;g++){await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let b of KU){await Jo(b);let x=await je(s.leadBox,{lines:tt}).catch(()=>"");if(!Cs(a?.agent,x)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<zU&&!r.canceled&&(await Jo(GU),!r.canceled);g++){let b=await je(s.leadBox,{lines:tt}).catch(()=>"");if(!Cs(a?.agent,b)){c=!0;break}await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=we(await ee(),e)||s,d=u?.boxes.find(g=>g.name===u.leadBox)||a,p=null,f=null;u&&d&&i&&c&&!r.canceled&&(p=await ZU(u,d).catch(()=>null),p||(f=await t4(u,d).catch(()=>null)));let w=u&&i&&!r.canceled?await YU(u).catch(()=>null):null,m=[p||f,...r.queuedTexts].filter(Boolean);u&&m.length&&(r.restored=wa(u,m),await ae()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await JU(u||s,d,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!p,lostRestored:!!f,queuedRestored:r.queuedTexts.length,shipDismissed:!!w,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await le({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:!!p,lostRestored:!!f,queuedRestored:r.queuedTexts.length,shipDismissed:!!w}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),fa.get(e)===r&&fa.delete(e),o()}),r}async function JU(e,t,n){await C(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function YU(e){let t=td(await K(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await C(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 ZU(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 XU){await Jo(r);let o=await je(e.leadBox,{lines:tt}).catch(()=>""),s=gi(t.agent,o);if(s&&Kl(n.text,s))return await ZE(e.leadBox,t.agent,n.text.length+8),Ie(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function t4(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>QU||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!He(r.text))return null;for(let s of e4){await Jo(s);let a;try{a=await om(t,{})}catch{return null}if(r0(a,r))return null;let i=await je(e.leadBox,{lines:tt}).catch(()=>"");if(gi(t.agent,i))return null}return Ie(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function n4(e,t,n,r=null){return ki({sessionName:e.leadBox,agent:t?.agent,agentCmd:ka(t?.agent),text:n,trackId:e.id,flow:"say",stats:r})}async function ZE(e,t,n,r=null){for(let o=0;o<3;o++){await As(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await Jo(200);let s=await je(e,{lines:tt}).catch(()=>""),a=gi(t,s);if(!a||r&&!Kl(r,a)&&!(t==="codex"&&$f(r,a)))return!0;n=a.length+8}return!1}function ma(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 wa(e,t){let n=[],r=o=>n.some(s=>He(s).includes(He(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?ma(e,n.join(`
|
|
215
217
|
|
|
216
|
-
`),{source:"restore"})?.text??null:null}function
|
|
218
|
+
`),{source:"restore"})?.text??null:null}function o4(e,t){let n=He(t);if(!n)return!1;if(Vo(e.id).some(o=>He(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>r4)break;if(!(s.type!=="said"||!s.text||He(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Fw(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=we(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=Fs(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&o4(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;He(o).includes(He(i))?ma(r,o):ma(r,`${i}
|
|
217
219
|
|
|
218
|
-
${o}`,{source:"restore"})}else
|
|
219
|
-
`);return`${
|
|
220
|
+
${o}`,{source:"restore"})}else ma(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Uw(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=we(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await ke(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=_v(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await ci(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await $r({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ka(o.agent)});let u=await Iv(a,r.leadBox,i);return Ie(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function QE(e){for(let t of e){if(!t||!await Us(t))continue;let n=await ov(t).catch(()=>null);if(!n)continue;if((await Q.stat(re.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function jt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:re.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function Ho(e){let t=[];for(let n of jt(e?.workspace)){if(!n?.dir)continue;let r=await Ii(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await $i(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function e_(e){let t=e;typeof t=="string"&&!re.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await ms(t)??await pl(t));let n=await QE([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 s4(e,t,n,r){let o=`track/${cg(t)}-${e}`,s=[];try{await Q.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await e_(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await _e(c).catch(()=>null),u=re.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${re.basename(c)}-${f}`;a.add(u);let d=re.join(n,u),p=await qs(c);await Ws(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await Tc(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Ot(i.base,i.dir).catch(()=>{});throw await Q.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Hw(e,t){let n=await ee(),r=we(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 e_(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await _e(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=re.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${re.basename(s)}-${p}`;let l=re.join(o.dir,c),u=await qs(s);await sv(s,o.branch)?await lu(s,l,o.branch):await Ws(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 Tc(o.dir,o.repos,o.branch),Ie(r,"retagged",{action:"add",repo:a??s,name:c,workspace:t_(o)}),await ae(),d}async function Ww(e,t){let n=await ee(),r=we(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 Pt.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 Ii(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await $i(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 Ot(s.base,s.dir).catch(()=>{}),await Q.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Tc(o.dir,o.repos,o.branch),Ie(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:t_(o)}),await ae(),{removed:s.name}}async function a4(e,t,{repo:n,repos:r,cwd:o}={}){await Q.mkdir(dc,{recursive:!0});let s=re.join(dc,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return s4(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!re.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ms(n)??await pl(n));let i=await QE([n,o,SU]);if(i){let c=`track/${cg(t)}-${e}`;try{let l=await qs(i);await Ws(i,s,c,l),await hc(s);let u=await _e(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await Q.mkdir(s,{recursive:!0}),await bm(s),await hc(s),{dir:s,base:null,branch:null,kind:"repo"}}async function hc(e){try{await Q.writeFile(re.join(e,"AGENTS.md"),uc,{flag:"wx"})}catch{return}await u4(e,["/AGENTS.md"])}function l4(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
221
|
+
`);return`${c4}
|
|
220
222
|
|
|
221
223
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
222
224
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -228,20 +230,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
228
230
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
229
231
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
230
232
|
or preview an app, run it from that app's own repo directory.
|
|
231
|
-
`}async function
|
|
233
|
+
`}async function Tc(e,t,n){let r=l4(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await Q.writeFile(re.join(e,o),r).catch(()=>{})}async function r_(e){let{stdout:t}=await HE("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return re.resolve(e,t.trim())}async function u4(e,t){try{let n=await r_(e);await Q.mkdir(re.dirname(n),{recursive:!0});let r=await Q.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
232
234
|
`).includes(s));o.length&&await Q.appendFile(n,(r.endsWith(`
|
|
233
235
|
`)||!r?"":`
|
|
234
236
|
`)+o.join(`
|
|
235
237
|
`)+`
|
|
236
|
-
`)}catch{}}async function
|
|
238
|
+
`)}catch{}}async function d4(e,t){try{let n=await r_(e),r=await Q.readFile(n,"utf8"),o=r.split(`
|
|
237
239
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
238
240
|
`).length&&await Q.writeFile(n,o.join(`
|
|
239
|
-
`))}catch{}}async function
|
|
241
|
+
`))}catch{}}async function qw(){let e={refreshed:0,removed:0,codexGlobal:null};try{await Q.mkdir(jn,{recursive:!0}),await Q.writeFile(re.join(jn,"CLAUDE.md"),i4).catch(()=>{}),await Q.writeFile(re.join(jn,"AGENTS.md"),uc).catch(()=>{});let t=process.env.CODEX_HOME||re.join(process.env.HOME||"/tmp",".codex");if((await Q.stat(t).catch(()=>null))?.isDirectory()){let r=re.join(t,"AGENTS.md"),o=await Q.readFile(r,"utf8").catch(()=>null);o===null?(await Q.writeFile(r,yw,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):gw(o)&&o!==yw&&(await Q.writeFile(r,yw).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[dc,ba]){let o=await Q.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=re.join(r,s.name),i=re.join(a,"AGENTS.md"),c=await Q.readFile(i,"utf8").catch(()=>null);c!==null&&gw(c)&&c!==uc&&(await Q.writeFile(i,uc).catch(()=>{}),e.refreshed++);let l=re.join(a,"CLAUDE.md"),u=await Q.readFile(l,"utf8").catch(()=>null);u!==null&&gw(u)&&(await Q.rm(l,{force:!0}).catch(()=>{}),await d4(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function o_(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of jt(t))n.base&&n.dir&&await Ot(n.base,n.dir).catch(()=>{});await Q.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await Ot(t.base,t.dir):await Q.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function vd({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 kc(o,s),i=await ee(),c=hd().slice(0,8),l=await a4(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(w=>(w.boxes||[]).map(m=>m.name))),d=`${e.trim()} lead`;u.has(Ml(d))&&(d=`${e.trim().slice(0,50).trim()} ${c} lead`);let p=await _r(d,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0}),f={id:c,name:e.trim(),cwd:p.cwd,workspace:l,leadBox:p.name,boxes:[eg(p,"lead","live")],events:[],createdAt:Date.now()};return Ie(f,"opened",{agent:o,box:p.name,skipPermissions:s,name:f.name,cwd:f.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:w,base:m,baseRef:g,repo:b})=>({name:w,base:m,baseRef:g,repo:b}))}:{}}}),i.tracks.push(f),await ae(),mi({cwd:p.cwd,sessionName:p.name,agent:o,agentCmd:a,onReady:async()=>{let w=f.boxes.find(m=>m.name===f.leadBox);!w||w.readyAt||(w.readyAt=Date.now(),await ae())}}),Nt(f)}async function Kw({provider:e="claude",fresh:t=!1}={}){if(!p4.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await ee()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Nt(r);if(u.boxes.find(p=>p.name===u.leadBox)?.state==="live")return u}await Yo(r.id).catch(()=>{})}let o=(await kc(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=hd().slice(0,8),a=re.join(jn,"setup",s);await Q.mkdir(a,{recursive:!0});let i=await _r(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&mi({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:[eg(i,"lead","live")],events:[],createdAt:Date.now()};return Ie(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await ee()).tracks.push(c),await ae(),Nt(c)}async function Gw(e,t,n){await Hp(t.cwd).catch(()=>{}),await yA(e).catch(()=>{});let r=ya.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),ya.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of Zo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await dg(a).catch(()=>{}),Pt.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 ee(),s=new Set((o.tracks||[]).filter(a=>a.id!==e&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of t.boxes)s.has(a.name)||await en(a.name).catch(()=>{})}async function zw(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await ee(),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 LA(s),Lw(e),await Gw(e,s,"track deleted"),n?await wE(e).catch(()=>{}):await hE(e).catch(()=>{}),await Ov(),await o_(s),t&&await FA(e).catch(()=>{}),t?await rE(e).catch(()=>{}):await nE(s).catch(()=>{}),Ie(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await ae(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Yo(e){let t=await ee(),n=we(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(),Lw(e),Ie(n,"archived",{boxes:n.boxes.length}),await ae(),n.kind==="setup"?(await s_(e),Nt(n)):(wc.add(e),h4(e).catch(()=>{}).finally(()=>wc.delete(e)),{...await Nt(n),archiving:!0})}async function h4(e){try{await w4(e)}catch{}await s_(e).catch(()=>{})}async function s_(e){let t=await ee(),n=we(t,e);if(!(!n||!n.archivedAt)){await Gw(e,n,"track archived");for(let r of n.boxes)eo.has(r.state)||(r.state="stopped");await ae()}}async function w4(e){let t=await ee(),n=we(t,e);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await ke(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Z0(n.leadBox,i.cwd,{timeoutMs:m4})),s="lead-cli"}catch{}if(r||(r=fx(n,await K(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await C(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 ae()}async function nr(e,t){let n=await at(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Vw(n);if(r.length){for(let o of r)await No(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Yo(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Vw(e){return(await Xn().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 rr(e,t){return Sd(e,t,"merged")}async function Xo(e,t){let n=await at(e,t).catch(()=>null);if(!n?.trackId)return null;let r={repo:n.repo??null,number:n.number,title:n.title??null,url:n.url??null},o=await Vw(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await sS(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=we(await ee(),n.trackId);if(!a)return null;let i=s[0];return Gi(await Nt(a),"pr.merged",{repo:n.repo??a.workspace?.repo??null,prs:s,prNumber:i?.number??null,prTitle:i?.title??null,prUrl:i?.url??null}).catch(()=>null)}async function Sd(e,t,n="merged"){let r=await at(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 Vw(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await oS(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await Xv(r.repo,r.number).catch(()=>!0))return!1;try{return await Dt(r.trackId,a_(a),{kind:"merge-notify"}),!0}catch{return await Po(r.repo,r.number,!0).catch(()=>{}),!1}}function a_(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 ${BE[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 ${BE[i.outcome]??i.outcome}`).join(`
|
|
240
242
|
`),a=n.length?r.length?`This is an automated merge notification: pick up whatever follow-up work the merged PR${n.length===1?"":"s"} unblock${n.length===1?"s":""} \u2014 and note the unmerged one${r.length===1?"":"s"}: that work did NOT land, so check whether it still needs to ship. If nothing needs doing, say so briefly.`:"This is an automated merge notification: pick up whatever follow-up work these merges unblock (dependent tasks, cleanup, next steps that were waiting on them). If nothing needs doing, say so briefly.":"This is an automated notification: NONE of the PRs landed \u2014 the work did not ship. Check whether it still needs to go out another way. If not, say so briefly.";return`${o}
|
|
241
243
|
${s}
|
|
242
|
-
${a}`}async function Nw(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<ti()?!1:Un(e.name)}async function cc(e,t,n){return!t||e.archivedAt||zr.has(t.state)?!1:(await ue({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await tn(t.name).catch(()=>{}),t.state="stopped",wc(e))}async function wc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=sw.get(r.name);if(o)return o;let s=hU(e,r,{freshSession:t,reason:n}).finally(()=>sw.delete(r.name));return sw.set(r.name,s),s}async function hU(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||zr.has(t.state)||await ve(t.name))return!1;let o=n===!0,s=o?qo():null,a=o?Qa(mw(t.agent),s,t.name):hw(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await vl(a);if(i===gs)return Ie(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===ys&&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(da(t)))}:null,l=Date.now();if(o){let u=da(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 vr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Na(u.hookToken),li({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&Ie(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),Ie(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Ie(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function md(e,{freshSession:t=!1}={}){let n=await ne(),r=ke(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,ic.delete(e),Ie(r,"unarchived",{}),await wc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ce(),Nt(r)}async function Pw(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>dw)throw new Error(`name too long (max ${dw} chars)`);let r=await ne(),o=ke(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 Nt(o);let s=o.name;return o.name=n,Ie(o,"renamed",{from:s,to:n}),await ce(),Nt(o)}async function Dw(){let e=await ne(),t=0;for(let n of e.tracks||[])n.transferredTo&&tu(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ce(),t}async function jw(){let e=await ne(),t=new Set((await en()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ve(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ve(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await tn(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ie(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!zr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ce(),r}async function hd(e,{freshSession:t=!1}={}){let n=await ne(),r=ke(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`);Ww(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(zr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ve(r.leadBox)){let i=await De(r.leadBox,{lines:tt}).catch(()=>"");if(Nl(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await tn(r.leadBox),o.state="stopped"}let a=await wc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ce(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Nt(r)}async function Bw(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 G(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 hd(e),await C(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 aE(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return $u({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function da(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 sd(e,t){let n=await ne(),r=ke(n,e);r&&(r.handoff={...r.handoff||{},...t},await ce())}function IE(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 Fw(){let e=await ne(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=IE(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ve(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=da(l),p=d[d.length-1],f=p.agent,h=p.sessionId??null;p.endedAt=u,r.reason&&(p.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:Uo,at:u},Ie(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await ue({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await ue({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await tn(i).catch(()=>{}),a&&await tn(a).catch(()=>{})}return t&&await ce(),t}async function wd(e,t,n=!0,r=null){let o=pa.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=ke(await ne(),e);if(!s)throw new Error(`track "${e}" not found`);mc(e,"handoff");let a=await dc(t,n),i=Eo(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 ve(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:fE(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return pa.set(e,l),await sd(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),wU(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 sd(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ue({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ue({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function wU(e,t,n,r,o){let s=async(R,P={})=>{e.step=R,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${R}`),await sd(e.trackId,{step:R,toBox:e.toBox}),await ue({flow:"lead-handoff",phase:R,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:P.sessionName,...P})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await ne(),i=ke(a,e.trackId)?.boxes.find(R=>R.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",Eo(c))e.checkpoint="skipped-capped";else try{await Cf(e.fromBox,{timeoutMs:dE})}catch(R){e.checkpoint="reconstructed",await ue({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:R?.message||String(R)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Mf(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 p=await Ff(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let f=Pk(p.launchCmd);await Tr({cwd:n.cwd,sessionId:p.sessionId,sessionName:e.fromBox,agent:o,agentCmd:ma(o),launchEcho:f}).catch(R=>{if(R?.code!=="cli-not-running")throw R});let h=wf(await De(e.fromBox,{lines:tt}).catch(()=>""),f);if(!Is(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let R=hw(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Ff(e.fromBox,{agentCmd:R,agent:c,resumeSessionId:l,pinned:u});let P=await ne(),H=ke(P,e.trackId);if(H){let j=H.boxes.find(A=>A.name===e.fromBox);j&&(j.state="live"),Ie(H,"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 ce()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await ne(),g=ke(w,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let k=g.boxes.find(R=>R.name===e.fromBox);if(!k)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),y=da(k),I=y[y.length-1];I.endedAt=x,e.reason&&(I.endReason=e.reason),y.push({agent:o,sessionId:p.sessionId,pinned:p.pinned,startedAt:x,endedAt:null}),k.agent=o,k.sessionId=p.sessionId,k.pinned=p.pinned,k.state="live",k.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||Uo,at:x},Ie(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:p.sessionId,epoch:y.length-1,singleBox:!0,forced:p.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ce(),await s("done",{sessionName:e.fromBox}),await sd(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Hw(e){let t=fw.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await ne(),r=ke(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=pa.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Pt.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 fw.set(e,s),gU(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 gU(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ve(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Eo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Cf(t.leadBox,{timeoutMs:dE})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Mf(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 jt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await ln(u.dir,["push","origin",o],pw);let{stdout:p}=await ln(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
243
|
-
`)[0]}s.push(d)}e.step="wip";let a=await
|
|
244
|
-
`)[0]}else l?.sha&&(A="wip snapshots cover single-repo worktrees only");await JA({id:e,cwd:y}).catch(()=>({restored:!1}));let N=te.join(y,Uo),z=null;(await hl(y)).restored?z="checkpoint":typeof u=="string"&&u.trim()&&(await Q.mkdir(te.dirname(N),{recursive:!0}),await Q.writeFile(N,u),z="brief");let T=new Set(k.tracks.flatMap(V=>(V.boxes||[]).map(de=>de.name))),M=null;f&&!T.has(f)&&!await ve(f).catch(()=>null)?M=f:(M=`${t.trim()} lead`,T.has(Sl(M))&&(M=`${t.trim().slice(0,50).trim()} ${e} lead`));let W=await vr(M,y,{agentCmd:g,agent:n});li({cwd:W.cwd,sessionName:W.name,agent:n,agentCmd:g});let K=Date.now(),be=Lw(W,"lead","live"),se=(Array.isArray(d)?d:[]).filter(V=>V&&typeof V=="object").map(V=>({...V})),we=se[se.length-1];we&&we.endedAt==null&&(we.endedAt=K,we.endReason==null&&(we.endReason=o==="recover"?"node-offline":"node-transfer")),se.push({agent:n,sessionId:W.sessionId??null,pinned:W.pinned===!0,startedAt:K,endedAt:null}),be.epochs=se;let ae={id:e,name:t.trim(),cwd:W.cwd,workspace:I,leadBox:W.name,boxes:[be],events:[],createdAt:Number.isFinite(p)?p:K,...z?{pendingHandoffHydrate:{artifact:Uo,at:K}}:{}};return Ie(ae,"transferred-in",{box:W.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(w)?w:null}:{},branch:I.branch??null,wip:j,...O?{wipAdvanced:O}:{},epoch:se.length-1,name:ae.name,cwd:ae.cwd,workspace:{base:I.base??null,branch:I.branch??null,kind:I.kind,...I.baseRef?{baseRef:I.baseRef}:{},...I.repo?{repo:I.repo}:{},...I.repos?{repos:I.repos.map(({name:V,base:de,baseRef:re,repo:Y})=>({name:V,base:de,baseRef:re,repo:Y}))}:{}}}),k.tracks.push(ae),await ce(),{...await Nt(ae),adopt:{wipApplied:j,wipSkipped:A,...O?{wipAdvanced:O}:{},codeless:R,artifactSource:z}}}catch(H){for(let j of P)await Ot(j.base,j.dir).catch(()=>{});throw await Q.rm(y,{recursive:!0,force:!0}).catch(()=>{}),H}}async function $E(e,t,n){let r=await ne(),o=ke(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ce())}async function ia(e,t){e.step=t,await gc(e)}function RE(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 OE(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function TU(e,t,n){if(!OE(n)||Pt.has(t)||!await ve(n.box).catch(()=>null))return!1;try{await Q.access(n.dir)}catch{return!1}let o=await ve(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Pt.set(t,s),ME(s,o).catch(a=>CE(s,a)),!0}async function Kw(){let e=await ne(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Pt.has(r))continue;if(await TU(n,r,o)){t=!0,Ie(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ue({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=RE(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!zr.has(i.state)&&(i.state="canceled"),Ie(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await ue({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await cE(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||zr.has(r.state)||Pt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?te.join(fa,`${n.id}-${r.subtaskId}`):null;await ue({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await cE(n,r.name,o)}}return t=await AU(e)||t,t&&await ce(),t}async function cE(e,t,n){if(t&&await tn(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of jt(e.workspace)){let s=te.join(n,o.name);await Ot(o.base,s).then(()=>!0,()=>!1)||await Ot(o.dir,s).catch(()=>{})}await Q.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ve(e.leadBox).catch(()=>null);r?.cwd&&await Ot(r.cwd,n).catch(()=>{})}async function AU(e){let t;try{t=await Q.readdir(fa)}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 Pt.values())o.dir&&n.add(te.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=te.join(fa,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of jt(a.workspace))await Ot(i.base,te.join(s,i.name)).catch(()=>{});await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ve(a.leadBox).catch(()=>null):null;i?.cwd?await Ot(i.cwd,s).catch(()=>{}):await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ue({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function zw(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Ds(n))throw new Error("task (string) required");let s=await dc(t,r),a=Eo(t);if(a){let f=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 f.usageCap=a,f}let i=ke(await ne(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ve(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 f=jt(i.workspace);if(!f.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let h of f)if(!h?.dir||!await js(h.dir))throw new Error(`tagged checkout "${h?.name??"?"}" is missing from the lead workspace (${h?.dir??"no dir"})`);u=f.map(h=>({name:h.name,leadDir:h.dir,base:null}))}else if(!await js(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=qo().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:Ds(n),skipPermissions:r,reason:fE(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Vw(i.name)}-${d}`,base:null,repos:u,dir:te.join(fa,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Pt.set(d,p),await gc(p),EU(p,c,s).catch(f=>CE(p,f)),p}async function CE(e,t){e.failedStep=e.step,e.error=t instanceof lc?"canceled":t?.message||String(t),e.step=t instanceof lc?"canceled":"error",e.finishedAt=Date.now(),await Jw(e).catch(()=>{}),await gc(e).catch(()=>{}),await RU(e).catch(()=>{})}function bd(e){let t=Pt.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 Yw(e){let t=Pt.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 EU(e,t,n){if(await ia(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Bs(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await gc(e)}else e.base=await Bs(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await ia(e,"spawn"),await Q.mkdir(fa,{recursive:!0}),e.repos){await Q.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Ls(a.leadDir,te.join(e.dir,a.name),e.branch,a.base);let s=jt((ke(await ne(),e.trackId)||{}).workspace);await hc(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 Ls(t.cwd,e.dir,e.branch,e.base),await ac(e.dir);await _U(e,t);let r=e.trackName.slice(0,38).trim(),o=await vr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await IU(e,o),await mE(e.trackId,o.name,AF(e.task,!!e.repos),{kind:"subtask"}),await ME(e,t)}async function ME(e,t){await ia(e,"working");let n=te.join(e.dir,od);if(await CU(n,e),e.canceled)throw new lc;if(await ia(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=te.join(e.dir,l.name);await cm(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:aE({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await dm(l.leadDir,l.base,e.branch,aa),l.files=await pm(l.leadDir,l.base,e.branch,aa);let d=await fm(l.leadDir,l.base,e.branch,aa);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(p=>te.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:te.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await cm(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:aE({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await dm(t.cwd,e.base,e.branch,aa),e.files=await pm(t.cwd,e.base,e.branch,aa);let s=await fm(t.cwd,e.base,e.branch,aa);e.applied=s.applied,e.conflicts=s.conflicts}await gc(e);let r=await OU(e,t,n);await ia(e,"resume"),await mE(e.trackId,e.leadBox,EF(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await ia(e,"reap"),await Jw(e),e.step="done";let o=Date.now();await $E(e.trackId,e.id,{...yd(e),finishedAt:o}),await $U(e,o),e.finishedAt=o}async function _U(e,t){let n=te.join(e.dir,te.dirname(nc));await Q.mkdir(n,{recursive:!0});let r="";try{r=await yk(t.cwd,t.sessionId)}catch{}if(!r)try{r=await Q.readFile(te.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
244
|
+
${a}`}async function Jw(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<ai()?!1:Kn(e.name)}async function gc(e,t,n){return!t||e.archivedAt||eo.has(t.state)?!1:(await le({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await en(t.name).catch(()=>{}),t.state="stopped",Ac(e))}async function Ac(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=bw.get(r.name);if(o)return o;let s=g4(e,r,{freshSession:t,reason:n}).finally(()=>bw.delete(r.name));return bw.set(r.name,s),s}async function g4(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||eo.has(t.state)||await ke(t.name))return!1;let o=n===!0,s=o?hd():null,a=o?oi(Iw(t.agent),s,t.name):$w(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Cl(a);if(i===vs)return Ie(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===Ss&&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(ga(t)))}:null,l=Date.now();if(o){let u=ga(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 _r(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=La(u.hookToken),mi({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&Ie(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),Ie(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Ie(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Td(e,{freshSession:t=!1}={}){let n=await ee(),r=we(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,wc.delete(e),Ie(r,"unarchived",{}),await Ac(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Nt(r)}async function Yw(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Aw)throw new Error(`name too long (max ${Aw} chars)`);let r=await ee(),o=we(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 Nt(o);let s=o.name;return o.name=n,Ie(o,"renamed",{from:s,to:n}),await ae(),Nt(o)}async function Xw(){let e=await ee(),t=0;for(let n of e.tracks||[])n.transferredTo&&du(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ae(),t}async function Zw(){let e=await ee(),t=new Set((await Qt()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ve(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ve(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await en(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ie(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!eo.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function Ad(e,{freshSession:t=!1}={}){let n=await ee(),r=we(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`);og(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(eo.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ke(r.leadBox)){let i=await je(r.leadBox,{lines:tt}).catch(()=>"");if(ql(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await en(r.leadBox),o.state="stopped"}let a=await Ac(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ae(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Nt(r)}async function Qw(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 K(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 Ad(e),await C(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 LE(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return Lu({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function ga(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 md(e,t){let n=await ee(),r=we(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function i_(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 tg(){let e=await ee(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=i_(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ke(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=ga(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let m=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:m,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=m,l.state="live",n.pendingHandoffHydrate={artifact:zo,at:u},Ie(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:w,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 le({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 le({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await en(i).catch(()=>{}),a&&await en(a).catch(()=>{})}return t&&await ae(),t}async function Ed(e,t,n=!0,r=null){let o=ya.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=we(await ee(),e);if(!s)throw new Error(`track "${e}" not found`);Sc(e,"handoff");let a=await kc(t,n),i=Co(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await ke(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:GE(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ya.set(e,l),await md(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),y4(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 md(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),le({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),le({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function y4(e,t,n,r,o){let s=async(O,M={})=>{e.step=O,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${O}`),await md(e.trackId,{step:O,toBox:e.toBox}),await le({flow:"lead-handoff",phase:O,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:M.sessionName,...M})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await ee(),i=we(a,e.trackId)?.boxes.find(O=>O.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",Co(c))e.checkpoint="skipped-capped";else try{await Hf(e.fromBox,{timeoutMs:qE})}catch(O){e.checkpoint="reconstructed",await le({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:O?.message||String(O)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Wf(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 p=await Yf(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let f=e0(p.launchCmd);await $r({cwd:n.cwd,sessionId:p.sessionId,sessionName:e.fromBox,agent:o,agentCmd:ka(o),launchEcho:f}).catch(O=>{if(O?.code!=="cli-not-running")throw O});let w=Ef(await je(e.fromBox,{lines:tt}).catch(()=>""),f);if(!Ms(o,w)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let O=$w(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Yf(e.fromBox,{agentCmd:O,agent:c,resumeSessionId:l,pinned:u});let M=await ee(),H=we(M,e.trackId);if(H){let P=H.boxes.find(E=>E.name===e.fromBox);P&&(P.state="live"),Ie(H,"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 ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let m=await ee(),g=we(m,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let b=g.boxes.find(O=>O.name===e.fromBox);if(!b)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),y=ga(b),$=y[y.length-1];$.endedAt=x,e.reason&&($.endReason=e.reason),y.push({agent:o,sessionId:p.sessionId,pinned:p.pinned,startedAt:x,endedAt:null}),b.agent=o,b.sessionId=p.sessionId,b.pinned=p.pinned,b.state="live",b.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||zo,at:x},Ie(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:p.sessionId,epoch:y.length-1,singleBox:!0,forced:p.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ae(),await s("done",{sessionName:e.fromBox}),await md(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function rg(e){let t=_w.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await ee(),r=we(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=ya.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Pt.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 _w.set(e,s),b4(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 b4(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ke(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Co(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Hf(t.leadBox,{timeoutMs:qE})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Wf(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 jt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await ln(u.dir,["push","origin",o],Ew);let{stdout:p}=await ln(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
245
|
+
`)[0]}s.push(d)}e.step="wip";let a=await aw({id:t.id,cwd:t.cwd},{force:!0}),i=await ee(),c=we(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&ga(l),await ae(),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:p})=>({name:u,repo:d,baseSha:p})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:p})=>({name:u,repo:d,error:p})),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 og(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 _d(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await ee(),o=we(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Lw(e),await Gw(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();du(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await iw({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,Ie(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await ae(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function sg(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await _d(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 FE(e,t,n){try{await lu(e,t,n)}catch{await ln(e,["worktree","prune"]).catch(()=>{}),await lu(e,t,n)}return t}function x4(e){if(typeof e!="string"||!k4.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function ag(e={}){let{trackId:t,name:n,agent:r="claude",skipPermissions:o=!0,mode:s="transfer"}=e;if(!t||typeof t!="string"||!/^[0-9a-f]{8}$/.test(t))throw new Error("trackId (8-hex string) required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");if(s!=="transfer"&&s!=="recover")throw new Error(`unknown adopt mode "${s}"`);if(e.branch!=null&&x4(e.branch),kw.has(t))throw new Error("an adopt is already running for this Track");kw.set(t,{startedAt:Date.now()});try{return await v4({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{kw.delete(t)}}async function v4({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:p=null,leadBox:f=null,reason:w=null,downSince:m=null}){let g=await kc(n,r),b=await ee(),x=we(b,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await iw({id:x.id,cwd:x.cwd}).catch(()=>{}),await o_(x),b.tracks.splice(b.tracks.indexOf(x),1),await ae()),await Q.mkdir(dc,{recursive:!0});let y=re.join(dc,e);await Q.rm(y,{recursive:!0,force:!0}).catch(()=>{});let $=null,O=!1,M=[];try{let H=[];if(i)for(let W of Array.isArray(c)?c:[]){let te=typeof W=="string"?W:W?.repo;if(!te||!/^[^/\s]+\/[^/\s]+$/.test(te))continue;let ie=await ms(te)??await pl(te);await ln(ie,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Ew),H.push({slug:te,base:ie,name:typeof W=="object"?W?.name??null:null})}if(i&&H.length===1){let{base:W,slug:te}=H[0];await FE(W,y,i),M.push({base:W,dir:y}),await hc(y);let ie=await qs(W);$={dir:y,base:W,branch:i,kind:"worktree",baseRef:ie,repo:te}}else if(i&&H.length>=2){await Q.mkdir(y,{recursive:!0});let W=new Set([".bostrat"]),te=[];for(let ie of H){let J=ie.name||re.basename(ie.base);W.has(J)&&(J=ie.slug.replace("/","-"));for(let Me=2;W.has(J);Me++)J=`${re.basename(ie.base)}-${Me}`;W.add(J);let ge=re.join(y,J);await FE(ie.base,ge,i),M.push({base:ie.base,dir:ge});let Oe=await qs(ie.base);te.push({name:J,dir:ge,base:ie.base,baseRef:Oe,repo:ie.slug})}await Tc(y,te,i),$={dir:y,kind:"multi",branch:i,repos:te}}else O=!0,await Q.mkdir(y,{recursive:!0}),await bm(y),await hc(y),$={dir:y,base:null,branch:null,kind:"repo"};let P=!1,E=null,R=null;if(l?.sha&&$.kind==="worktree")try{await ln(y,["fetch","origin",sw(e)],Ew);let{stdout:W}=await ln(y,["rev-parse","FETCH_HEAD^"]),{stdout:te}=await ln(y,["rev-parse","HEAD"]),ie=W.trim()===te.trim();ie||await ln(y,["merge-base","--is-ancestor",te.trim(),W.trim()]).then(()=>!0,()=>!1)&&(await ln(y,["reset","--hard",W.trim()]),R={from:te.trim(),to:W.trim()},ie=!0),ie?(await ln(y,["read-tree","--reset","-u","FETCH_HEAD"]),await ln(y,["reset","-q"]),P=!0):E="snapshot does not parent the fetched branch head"}catch(W){E=String(W?.message||W).split(`
|
|
246
|
+
`)[0]}else l?.sha&&(E="wip snapshots cover single-repo worktrees only");await _E({id:e,cwd:y}).catch(()=>({restored:!1}));let G=re.join(y,zo),V=null;(await Al(y)).restored?V="checkpoint":typeof u=="string"&&u.trim()&&(await Q.mkdir(re.dirname(G),{recursive:!0}),await Q.writeFile(G,u),V="brief");let _=new Set(b.tracks.flatMap(W=>(W.boxes||[]).map(te=>te.name))),N=null;f&&!_.has(f)&&!await ke(f).catch(()=>null)?N=f:(N=`${t.trim()} lead`,_.has(Ml(N))&&(N=`${t.trim().slice(0,50).trim()} ${e} lead`));let U=await _r(N,y,{agentCmd:g,agent:n});mi({cwd:U.cwd,sessionName:U.name,agent:n,agentCmd:g});let z=Date.now(),ue=eg(U,"lead","live"),pe=(Array.isArray(d)?d:[]).filter(W=>W&&typeof W=="object").map(W=>({...W})),Se=pe[pe.length-1];Se&&Se.endedAt==null&&(Se.endedAt=z,Se.endReason==null&&(Se.endReason=o==="recover"?"node-offline":"node-transfer")),pe.push({agent:n,sessionId:U.sessionId??null,pinned:U.pinned===!0,startedAt:z,endedAt:null}),ue.epochs=pe;let Ae={id:e,name:t.trim(),cwd:U.cwd,workspace:$,leadBox:U.name,boxes:[ue],events:[],createdAt:Number.isFinite(p)?p:z,...V?{pendingHandoffHydrate:{artifact:zo,at:z}}:{}};return Ie(Ae,"transferred-in",{box:U.name,agent:n,fromNode:s,toNode:a,mode:o,reason:w,...o==="recover"?{downSince:Number.isFinite(m)?m:null}:{},branch:$.branch??null,wip:P,...R?{wipAdvanced:R}:{},epoch:pe.length-1,name:Ae.name,cwd:Ae.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:W,base:te,baseRef:ie,repo:J})=>({name:W,base:te,baseRef:ie,repo:J}))}:{}}}),b.tracks.push(Ae),await ae(),{...await Nt(Ae),adopt:{wipApplied:P,wipSkipped:E,...R?{wipAdvanced:R}:{},codeless:O,artifactSource:V}}}catch(H){for(let P of M)await Ot(P.base,P.dir).catch(()=>{});throw await Q.rm(y,{recursive:!0,force:!0}).catch(()=>{}),H}}async function c_(e,t,n){let r=await ee(),o=we(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function pa(e,t){e.step=t,await Ec(e)}function l_(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 u_(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function E4(e,t,n){if(!u_(n)||Pt.has(t)||!await ke(n.box).catch(()=>null))return!1;try{await Q.access(n.dir)}catch{return!1}let o=await ke(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Pt.set(t,s),p_(s,o).catch(a=>d_(s,a)),!0}async function ig(){let e=await ee(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Pt.has(r))continue;if(await E4(n,r,o)){t=!0,Ie(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await le({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=l_(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!eo.has(i.state)&&(i.state="canceled"),Ie(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await le({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await UE(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||eo.has(r.state)||Pt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?re.join(ba,`${n.id}-${r.subtaskId}`):null;await le({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await UE(n,r.name,o)}}return t=await _4(e)||t,t&&await ae(),t}async function UE(e,t,n){if(t&&await en(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of jt(e.workspace)){let s=re.join(n,o.name);await Ot(o.base,s).then(()=>!0,()=>!1)||await Ot(o.dir,s).catch(()=>{})}await Q.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ke(e.leadBox).catch(()=>null);r?.cwd&&await Ot(r.cwd,n).catch(()=>{})}async function _4(e){let t;try{t=await Q.readdir(ba)}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 Pt.values())o.dir&&n.add(re.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=re.join(ba,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of jt(a.workspace))await Ot(i.base,re.join(s,i.name)).catch(()=>{});await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ke(a.leadBox).catch(()=>null):null;i?.cwd?await Ot(i.cwd,s).catch(()=>{}):await Q.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await le({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function lg(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Fs(n))throw new Error("task (string) required");let s=await kc(t,r),a=Co(t);if(a){let f=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 f.usageCap=a,f}let i=we(await ee(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ke(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let f=jt(i.workspace);if(!f.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let w of f)if(!w?.dir||!await Us(w.dir))throw new Error(`tagged checkout "${w?.name??"?"}" is missing from the lead workspace (${w?.dir??"no dir"})`);u=f.map(w=>({name:w.name,leadDir:w.dir,base:null}))}else if(!await Us(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=hd().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:Fs(n),skipPermissions:r,reason:GE(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${cg(i.name)}-${d}`,base:null,repos:u,dir:re.join(ba,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Pt.set(d,p),await Ec(p),I4(p,c,s).catch(f=>d_(p,f)),p}async function d_(e,t){e.failedStep=e.step,e.error=t instanceof yc?"canceled":t?.message||String(t),e.step=t instanceof yc?"canceled":"error",e.finishedAt=Date.now(),await dg(e).catch(()=>{}),await Ec(e).catch(()=>{}),await C4(e).catch(()=>{})}function $d(e){let t=Pt.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 ug(e){let t=Pt.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 I4(e,t,n){if(await pa(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Hs(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Ec(e)}else e.base=await Hs(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await pa(e,"spawn"),await Q.mkdir(ba,{recursive:!0}),e.repos){await Q.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Ws(a.leadDir,re.join(e.dir,a.name),e.branch,a.base);let s=jt((we(await ee(),e.trackId)||{}).workspace);await Tc(e.dir,e.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),e.branch)}else await Ws(t.cwd,e.dir,e.branch,e.base),await hc(e.dir);await $4(e,t);let r=e.trackName.slice(0,38).trim(),o=await _r(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await R4(e,o),await zE(e.trackId,o.name,AU(e.task,!!e.repos),{kind:"subtask"}),await p_(e,t)}async function p_(e,t){await pa(e,"working");let n=re.join(e.dir,fd);if(await N4(n,e),e.canceled)throw new yc;if(await pa(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=re.join(e.dir,l.name);await ym(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:LE({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await xm(l.leadDir,l.base,e.branch,da),l.files=await vm(l.leadDir,l.base,e.branch,da);let d=await Sm(l.leadDir,l.base,e.branch,da);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(p=>re.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:re.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await ym(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:LE({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await xm(t.cwd,e.base,e.branch,da),e.files=await vm(t.cwd,e.base,e.branch,da);let s=await Sm(t.cwd,e.base,e.branch,da);e.applied=s.applied,e.conflicts=s.conflicts}await Ec(e);let r=await M4(e,t,n);await pa(e,"resume"),await zE(e.trackId,e.leadBox,EU(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await pa(e,"reap"),await dg(e),e.step="done";let o=Date.now();await c_(e.trackId,e.id,{...Id(e),finishedAt:o}),await O4(e,o),e.finishedAt=o}async function $4(e,t){let n=re.join(e.dir,re.dirname(pc));await Q.mkdir(n,{recursive:!0});let r="";try{r=await jk(t.cwd,t.sessionId)}catch{}if(!r)try{r=await Q.readFile(re.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
245
247
|
|
|
246
248
|
**Delegated task:** ${e.task}
|
|
247
249
|
|
|
@@ -252,15 +254,15 @@ ${a}`}async function Nw(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
252
254
|
## Conversation so far (lead \u2192 you)
|
|
253
255
|
|
|
254
256
|
${r}
|
|
255
|
-
`;await Q.writeFile(
|
|
257
|
+
`;await Q.writeFile(re.join(e.dir,pc),o)}async function R4(e,t){let n=await ee(),r=we(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:La(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Ie(r,"subtask-delegated",{to:t.name,toAgent:e.agent,task:e.task,subtaskId:e.id,skipPermissions:e.skipPermissions,reason:e.reason}),r.subtasks=r.subtasks||{},r.subtasks[e.id]={...r.subtasks[e.id]||{},...Id(e)},await ae())}async function O4(e,t=e.finishedAt){let n=await ee(),r=we(n,e.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=e.conflicts.length?"conflict":"merged",o.stat=e.stat),Ie(r,"subtask-merged",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,stat:e.stat,conflicts:e.conflicts.length,files:e.files,durationMs:t?t-e.startedAt:null}),await ae()}async function C4(e){let t=await ee(),n=we(t,e.trackId);if(!n)return;let r=e.canceled||e.error==="canceled",o=n.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=r?"canceled":"failed"),Ie(n,r?"subtask-canceled":"subtask-failed",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,failedStep:e.failedStep||e.step,error:e.error,durationMs:e.finishedAt?e.finishedAt-e.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[e.id]={...n.subtasks[e.id]||{},...Id(e)},await ae()}async function M4(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=re.join(t.cwd,r);await Q.mkdir(re.dirname(o),{recursive:!0}),await Q.copyFile(n,o)}catch{}return r}async function dg(e){if(e.dir&&await Hp(e.dir).catch(()=>{}),e.box&&await en(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await Ot(t.leadDir,re.join(e.dir,t.name)).catch(()=>{});await Q.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await ke(e.leadBox).catch(()=>null))?.cwd;n&&await Ot(n,e.dir).catch(()=>{})}}async function N4(e,t){let n=(t.startedAt||Date.now())+S4;for(;;){if(t.canceled||t.forceMerge)return;try{await Q.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Jo(T4)}}function Jo(e){return new Promise(t=>setTimeout(t,e))}var HE,jn,WE,vU,dc,SU,wd,bc,Qr,zo,qE,TU,fd,pc,AU,EU,Vt,fw,we,mw,Ie,hw,ww,$U,eo,DE,CU,MU,NU,PU,BU,ha,jE,WU,qU,KU,GU,zU,fa,fc,XU,QU,e4,r4,t_,mc,n_,i4,uc,gw,c4,yw,p4,wc,f4,m4,BE,bw,Aw,eg,ya,xa,Ew,ln,_w,ng,k4,kw,S4,T4,ba,da,Pt,A4,Zo,Id,Ec,cg,yc,or=q(()=>{Va();ct();Kf();mx();hi();vr();Fl();bx();pt();kn();ds();Ll();sm();vo();fm();hm();nv();yi();Rf();Ul();Lf();nn();uu();kr();An();wi();hu();$v();Rm();UT();Ql();Xl();WT();Sh();de();Eh();Uu();ed();Fi();Mo();rd();ca();Xh();ld();ow();Zn();zu();vE();cw();IE();El();CE();NE();pw();HE=xU(kU),jn=process.env.BOSTRAT_STATE_DIR||re.join(process.env.HOME||"/tmp",".bostrat"),WE=re.join(jn,"tracks.json"),vU=re.join(jn,"events.jsonl"),dc=re.join(jn,"workspaces"),SU=process.env.BOSTRAT_DEFAULT_REPO||null,wd={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"}},bc=Object.keys(wd);Qr={at:0,value:null};zo=".bostrat/handoff/latest.md",qE=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;TU=/\(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\.\)$/;fd=".bostrat/handoff/subtask-result.md",pc=".bostrat/handoff/lead-context.md",AU=(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 ${pc} \u2014 read that first for context. Your scoped task:
|
|
256
258
|
|
|
257
259
|
${e}
|
|
258
260
|
|
|
259
|
-
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${
|
|
261
|
+
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${fd} (at this directory's root) with sections \u2014 Result (what you did), Files (paths you changed, prefixed with the repo directory), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`:`You are a SUBTASK agent working in an isolated git worktree, delegated by a lead agent on a shared workstream. The lead's full working tree is already checked out here, and the conversation so far is summarized in ${pc} \u2014 read that first for context. Your scoped task:
|
|
260
262
|
|
|
261
263
|
${e}
|
|
262
264
|
|
|
263
|
-
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${
|
|
265
|
+
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 ${fd} 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.`,EU=(e,t,n,r)=>`A subtask box you delegated has finished: "${e}". Its changes are now merged into your working tree (${t.files} file${t.files===1?"":"s"}, +${t.insertions} \u2212${t.deletions}). Read ${r} for exactly what it did. `+(n.length?`IMPORTANT: ${n.length} merge conflict${n.length===1?"":"s"} need resolving \u2014 fix the conflict markers in: ${n.join(", ")}, then continue.`:"Review the merged changes and continue the work."),Vt=null,fw=null;we=(e,t)=>e.tracks.find(n=>n.id===t)||null,mw=Promise.resolve();Ie=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),_U(e.id,r),Cv(e,r),r};Uk(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await ee()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(Ie(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await ae().catch(()=>{}))});hw=re.join(jn,"plane-link","update-notice.json");ww=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),$U=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);eo=new Set(["merged","conflict","canceled","failed","handed-off"]),DE=new Map;CU=5e3;MU={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};NU=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];PU=10*6e4;BU=1500,ha=new Map;jE="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.";WU=4e3,qU=3,KU=[300,350,400],GU=1e3,zU=12,fa=new Map,fc=new Map;XU=[250,400,500,600];QU=3e4,e4=[350,650,900];r4=12e4;t_=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}))});mc="# bostrat workspace",n_=`${mc}
|
|
264
266
|
|
|
265
267
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
266
268
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -579,17 +581,19 @@ notifying about belongs in your chat message too; the notification is a nudge
|
|
|
579
581
|
toward the chat, never the only record.
|
|
580
582
|
|
|
581
583
|
When beginning a semantic git-recovery phase that Bostrat cannot observe at the
|
|
582
|
-
button boundary, declare it so the Track
|
|
584
|
+
button boundary, declare it so the Track's status names the work:
|
|
583
585
|
|
|
584
586
|
bostrat-status set fixing-ci --summary "Repairing the failing client test"
|
|
585
587
|
bostrat-status set reconcile --pr 641
|
|
586
588
|
bostrat-status set rebase
|
|
587
589
|
|
|
588
|
-
Clear it when the phase ends with \`bostrat-status clear\`.
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
590
|
+
Clear it when the phase ends with \`bostrat-status clear\`. The pill itself stays
|
|
591
|
+
\`working\` \u2014 the vocabulary carries no per-integration verbs; your declaration is
|
|
592
|
+
what its tooltip reads. Declarations are non-blocking, apply only to THIS Track,
|
|
593
|
+
and never replace a real question or approval \u2014 those still read needs you. Use
|
|
594
|
+
\`bostrat-status\` with no arguments to inspect the folded status. Do not declare
|
|
595
|
+
ordinary work; Bostrat already derives working from the live turn, waiting from
|
|
596
|
+
pending automation, and settled when no obligation remains.
|
|
593
597
|
|
|
594
598
|
When you FINISH a piece of implementation work (feature built, bug fixed, checks
|
|
595
599
|
green) and the operator has not already told you to ship it: do NOT commit, push,
|
|
@@ -636,23 +640,23 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
636
640
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
637
641
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
638
642
|
follow-up on its own; the operator's tap is the gate.
|
|
639
|
-
`,
|
|
640
|
-
`)),
|
|
641
|
-
`,`${
|
|
643
|
+
`,i4=n_,uc=n_+I0;gw=e=>typeof e=="string"&&(e===mc||e.startsWith(`${mc}
|
|
644
|
+
`)),c4="# bostrat multi-repo workspace";yw=uc.replace(`${mc}
|
|
645
|
+
`,`${mc}
|
|
642
646
|
|
|
643
647
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
644
648
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
645
649
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
646
650
|
When working anywhere else, ignore everything below.
|
|
647
|
-
`);uU=new Set(["claude","codex","cursor"]);ic=new Set,dU=e=>ic.has(e);pU=9e4;sE={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};sw=new Map;dw=120;Lw=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Na(e.hookToken),createdAt:Date.now()});pa=new Map,ha=e=>pa.get(e)||(Yt?ke(Yt,e)?.handoff:null)||null;pw=6e4,ln=(e,t,n=15e3)=>lE("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),fw=new Map,Uw=e=>fw.get(e)||null;yU=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;aw=new Map;xU=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,vU=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,fa=te.join(Mn,"worktrees"),aa=[nc,od],Pt=new Map,SU=e=>Pt.get(e)||(Yt?Yt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Vo=e=>{let t=new Map([...Pt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Yt?ke(Yt,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)},yd=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});gc=e=>$E(e.trackId,e.id,yd(e));Vw=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",lc=class extends Error{}});dt();import Ae from"node:path";import ht from"node:fs";import u5 from"node:readline";import{fileURLToPath as d5}from"node:url";import Ut from"express";import p5 from"node:http";import f5 from"node:https";import ip from"node:os";import{randomUUID as m5}from"node:crypto";import{WebSocketServer as h5}from"ws";dt();import VC from"node-pty";async function Jb(e,t,n={}){if(!Fe(t)){_l(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await xr()){_l(e,`host tmux server not running on ${Rt}`),e.close(1011,"tmux server unavailable");return}if(!await ve(t)){_l(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Yb(n.cols,120,Xb),o=Yb(n.rows,32,Zb),s;try{s=VC.spawn(X,["-S",Rt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){_l(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Kp(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Kp(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"&&XC(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{}JC(t)})}var zC=6,YC=250;async function JC(e,t=zC,n=YC){for(let r=0;r<t;r++){if(await fo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Xb=20,Zb=6;function XC(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Xb&&Number(t)>=Zb}function Yb(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Kp(e,t){try{e.send(JSON.stringify(t))}catch{}}function _l(e,t){Kp(e,{type:"error",message:t})}function Qb(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}}rn();$l();Pf();er();import{createRequire as w5}from"node:module";var MU=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Xw({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(MU)&&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 KE from"node:fs";import Jr from"node:fs/promises";import JU from"node:os";import lt from"node:path";import{spawn as XU}from"node:child_process";import{createRequire as ZU}from"node:module";fe();su();import WE from"node:fs";import bc from"node:fs/promises";import DU from"node:http";import dn from"node:path";import xd from"node:os";import{createRequire as jU}from"node:module";import Qw from"ws";import NU from"node:path";import PU from"node:os";var yc=process.env.BOSTRAT_STATE_DIR||NU.join(process.env.HOME||PU.tmpdir(),".bostrat");function Zw(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 PE(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Zw(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function DE(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 un=(()=>{try{return jU(import.meta.url)("../package.json").version||null}catch{return null}})(),jE=process.env.BOSTRAT_STATE_DIR||dn.join(process.env.HOME||xd.tmpdir(),".bostrat");function zo({env:e=process.env,pkgDir:t=dn.resolve(dn.dirname(new URL(import.meta.url).pathname),".."),exists:n=WE.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(dn.join(t,"..",".git"))?"git-checkout":"dev"}function kc(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 kd(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 BU({home:e=process.env.HOME||xd.homedir(),env:t=process.env}={}){let n=r=>{try{return WE.statSync(r),!0}catch{return!1}};return{claude:n(dn.join(e,".claude",".credentials.json")),codex:n(dn.join(t.CODEX_HOME||dn.join(e,".codex"),"auth.json"))}}var ct=1,BE=1e3,LE=3e4,LU=6e4,FU=1500,UU=500,Yr=100,HU=2e3,WU=25e3,qU=6e4,qE=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},GU=qE(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),KU=3e4,FE=10*6e4,VU=400,UE=250,zU=qE(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),eg=4,YU=6e4,HE=6e4;function GE({url:e,token:t,nodeId:n=xd.hostname(),name:r=xd.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=YU,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:d=null,updatePolicy:p=null,installKind:f=zo(),selfUrl:h=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:w=null,onDegraded:g=null,onRecovered:k=null,onConnected:x=null,ingestMode:y=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:I=GU,unackedRetryMs:R=qU,pingMs:P=WU,maxInflightEvents:H=VU,httpTimeoutMs:j=zU,stallBackoffMinMs:A=KU,fetchImpl:O=globalThis.fetch,log:N=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=DE(e);let z=h.replace(/^http/,"ws"),$=new Set,T=new Map,M=new Map,W=dn.join(jE,"plane-link","cursor.json"),K=dn.join(jE,"plane-link","dead-letter.ndjson"),be=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),se={},we={},ae=new Map,V=null,de=null,re=0,Y=0,$e=0,Pe=Promise.resolve(),me="ws",_=0,F=!1,ge=0,Ne=!1,Be=Yr,St=new Set,hn=[],wt=null,b=null,m=!1,v=!1,D=BE,le=null,ye=null,Ue=null,gt=null,Ce=null,Ra=!1,Jc=0;async function TR(){try{se=JSON.parse(await bc.readFile(W,"utf8"))}catch{se={}}}function AR(){Ra=!0,!Ue&&(Ue=setTimeout(async()=>{if(Ue=null,!!Ra){Ra=!1;try{await bc.mkdir(dn.dirname(W),{recursive:!0}),await bc.writeFile(W,JSON.stringify(se))}catch(L){N.error("[plane-link] cursor flush failed:",L?.message||L)}}},HU),Ue.unref?.())}function at(L){return!b||b.readyState!==Qw.OPEN?!1:(b.send(JSON.stringify(L)),!0)}function Oa(L){let U=Date.now();for(let{trackId:E,event:B}of L){let J=ae.get(B.id);ae.set(B.id,{trackId:E,seq:B.seq,event:B,sentAt:U,firstSentAt:J?.firstSentAt??U,retryAt:U+R,tries:(J?.tries??0)+1,nacks:J?.nacks??0})}return me==="http"?($R(L),!0):at({v:ct,ch:"events",type:"append",events:L})}function ER(L){let U=null;for(let E of ae.values())E.trackId===L&&(U==null||E.seq<U)&&(U=E.seq);return U}function up(L){let U=we[L];if(U==null)return;let E=ER(L),B=E==null?U:Math.min(U,E-1);B>(se[L]??0)&&(se[L]=B,AR())}function Ca(){if(V=Date.now(),de!=null){let L=Date.now()-de;N.log(`[plane-link] spine sync recovered after ${Math.round(L/1e3)}s degraded (${re} stall redial(s))`);let U=de;de=null,re=0,Y=0,F=!1,ge=0;try{k?.({since:U,downMs:L})}catch(E){N.error(`[plane-link] onRecovered failed: ${E?.message||E}`)}}}function dp(L){if(!Array.isArray(L)||!L.length)return;let U=new Set;for(let E of L){let B=ae.get(E);B&&(ae.delete(E),(we[B.trackId]??0)<B.seq&&(we[B.trackId]=B.seq),U.add(B.trackId))}for(let E of U)up(E);Ca()}function Ty(L,U){$e++;let E={at:Date.now(),trackId:L.trackId,seq:L.seq,id:L.event?.id??null,type:L.event?.type??null,reason:U,event:L.event??null};N.error(`[plane-link] DEAD-LETTER ${L.trackId} seq ${L.seq} (${E.type??"?"}): plane rejected it permanently (${U}) \u2014 kept in ${K}`),Pe=Pe.then(async()=>{await bc.mkdir(dn.dirname(K),{recursive:!0}),await bc.appendFile(K,JSON.stringify(E)+`
|
|
648
|
-
`)}).catch(B=>N.error(`[plane-link] dead-letter write failed: ${B?.message||B}`))}function Ay(L,U,E,B=null){if(!Array.isArray(L)||!L.length)return;let J=new Set,Se=Date.now();for(let Ee of L){let He=ae.get(Ee);He&&(E?(ae.delete(Ee),Ty(He,U),(we[He.trackId]??0)<He.seq&&(we[He.trackId]=He.seq),J.add(He.trackId)):(He.nacks++,He.retryAt=Se+Math.max(1e3,Number(B)||R)))}for(let Ee of J)up(Ee);Ca()}function Ey(L=Date.now()){let U=1/0;for(let E of ae.values())E.firstSentAt<U&&(U=E.firstSentAt);return U===1/0?0:L-U}function _y(){if(!m||!ae.size)return;let L=Date.now(),U=Ey(L);if(U<I||V!=null&&L-V<I||me==="http"&&F||Y>L)return;re++;let E=Math.min(A*2**(re-1),FE);if(de==null){de=L-U,N.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(U/1e3)}s with ${ae.size} event(s) in flight (${me} door) \u2014 the plane is not answering appends`);try{g?.({since:de,unacked:ae.size,oldestAgeMs:U,mode:me})}catch(B){N.error(`[plane-link] onDegraded failed: ${B?.message||B}`)}}if(me==="http"){F=!0,ge=L+E,N.error(`[plane-link] ingest circuit OPEN (stall #${re}); probing /v1/ingest/health in ${Math.round(E/1e3)}s`);return}Y=L+E,N.error(`[plane-link] closing the link and redialing in ${Math.round(E/1e3)}s (stall redial #${re})`);try{b?.terminate()}catch{}}function pp(){if(!m)return;let L=Date.now();if(me==="http"&&F)return;let U=[...ae.values()].filter(B=>B.event&&B.retryAt<=L&&!(me==="http"&&St.has(B.trackId)));if(!U.length)return;U.sort((B,J)=>B.trackId===J.trackId?B.seq-J.seq:B.trackId<J.trackId?-1:1),N.log(`[plane-link] re-sending ${U.length} unacked event(s) the plane never confirmed`);let E=me==="http"?Be:Yr;if(me==="http"){let B=new Map;for(let J of U)(B.get(J.trackId)??B.set(J.trackId,[]).get(J.trackId)).push(J);for(let[,J]of B){if(_>=eg)return;Oa(J.slice(0,E).map(Se=>({trackId:Se.trackId,event:Se.event})))}return}for(let B=0;B<U.length;B+=E){let J=U.slice(B,B+E).map(Se=>({trackId:Se.trackId,event:Se.event}));if(!Oa(J))return}}async function _R(L=null){for(;;){if(!m)return!1;let U=ae.size<H,E=me!=="http"||_<eg&&!F&&(!L||!St.has(L));if(U&&E)return!0;await new Promise(B=>setTimeout(B,100))}}function fp(){if(wt=null,!m||!hn.length)return;let L=hn;if(hn=[],me!=="http"){for(let E=0;E<L.length;E+=Yr)Oa(L.slice(E,E+Yr));return}let U=new Map;for(let E of L)(U.get(E.trackId)??U.set(E.trackId,[]).get(E.trackId)).push(E);for(let[E,B]of U){if(St.has(E)||_>=eg||F){hn.push(...B);continue}for(let J=0;J<B.length;J+=Be)Oa(B.slice(J,J+Be))}hn.length&&!wt&&(wt=setTimeout(fp,UE),wt.unref?.())}let IR=L=>{let U=new Map;for(let{trackId:E,event:B}of L)(U.get(E)??U.set(E,[]).get(E)).push(B);return[...U].map(([E,B])=>({trackId:E,events:B}))},ss=(L,U,E,{progress:B})=>{let J=Date.now();for(let{event:Se}of L){let Ee=ae.get(Se.id);Ee&&(Ee.nacks++,Ee.retryAt=J+Math.max(1e3,E||R))}B&&Ca()};async function $R(L){let U=new Set(L.map(E=>E.trackId));for(let E of U)St.add(E);_++;try{let E=await O(`${be}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:IR(L)}),signal:AbortSignal.timeout(j)}),B=(Number(E.headers.get("retry-after"))||0)*1e3;if(E.status===200){let J=await E.json();Be=Yr;for(let Se of J.results??[]){dp(Se.acked??[]);let Ee=(He,Tt)=>{let Ht=new Map;for(let Qe of He??[])(Ht.get(Qe.reason)??Ht.set(Qe.reason,[]).get(Qe.reason)).push(Qe.id);for(let[Qe,Cy]of Ht)Ay(Cy,Qe,Tt,J.retryAfterMs??null)};Ee(Se.rejected,!0),Ee(Se.deferred,!1)}dp(J.discarded??[]),Ca();return}if(E.status===429){ss(L,"overloaded",B||5e3,{progress:!0});return}if(E.status===413&&L.length>1){Be=Math.max(1,Math.floor(L.length/2)),N.error(`[plane-link] ingest request too large (${L.length} events) \u2014 halving batches to ${Be}`),ss(L,"too_large",1e3,{progress:!0});return}if(E.status===413){let J=ae.get(L[0].event.id);J&&(ae.delete(J.event.id),Ty(J,"too_large"),(we[J.trackId]??0)<J.seq&&(we[J.trackId]=J.seq),up(J.trackId)),Ca();return}if(E.status===401||E.status===403){if(Date.now()-Jc>=HE){Jc=Date.now();let J="";try{J=(await E.json())?.error??""}catch{}N.error(`[plane-link] plane refused ingest (HTTP ${E.status}${J?`: ${J}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}ss(L,`http_${E.status}`,6e4,{progress:!1});return}if(E.status===400){N.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${L.length} event(s) for retry; this is a bug, not data`),ss(L,"http_400",6e4,{progress:!1});return}ss(L,`http_${E.status}`,B||5e3,{progress:!1})}catch(E){let B=E?.name==="TimeoutError"||E?.name==="AbortError";B&&L.length>1&&(Be=Math.max(1,Math.floor(L.length/2)),N.error(`[plane-link] ingest request of ${L.length} event(s) outlived its ${j} ms deadline \u2014 halving batches to ${Be}`)),ss(L,B?"timeout":"network",5e3,{progress:!1})}finally{_--;for(let E of U)St.delete(E)}}async function RR(){if(me!=="http"||!F||!m||Ne)return;let L=Date.now();if(!(ge>L)){Ne=!0;try{let U=await O(`${be}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(j)});if(!U.ok)throw new Error(`HTTP ${U.status}`);N.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),F=!1,ge=0,Y=Date.now()+j+R+1e3;for(let E of ae.values())E.retryAt=Math.min(E.retryAt,Date.now());pp()}catch(U){re++;let E=Math.min(A*2**(re-1),FE);ge=Date.now()+E,N.error(`[plane-link] ingest door still not answering (${U?.message||U}); next probe in ${Math.round(E/1e3)}s`)}finally{Ne=!1}}}function Iy(){let L=Date.now();return{mode:me,unacked:ae.size,oldestUnackedAgeMs:Ey(L),lastProgressAt:V,degradedSince:de,stallRedials:re,deadLettered:$e,http:me==="http"?{inFlight:_,circuitOpen:F,nextProbeInMs:F?Math.max(0,ge-L):0,batchCap:Be}:null}}async function $y(){if(!i)return;let L;try{L=await i()}catch(U){N.error("[plane-link] metrics sample failed:",U?.message||U);return}L&&at({v:ct,ch:"ctl",type:"metrics",metrics:{...L,planeLink:Iy()}})}function OR(){i&&(Ce&&clearInterval(Ce),$y(),Ce=setInterval($y,c),Ce.unref?.())}async function CR(){let L;try{L=await o()}catch(U){N.error("[plane-link] backfill listTracks failed:",U?.message||U);return}at({v:ct,ch:"ctl",type:"tracks",ids:L.filter(U=>!U.transferredTo).map(U=>U.id).filter(U=>typeof U=="string")});for(let U of L){if(!m)return;let E;try{E=await G(U)}catch{continue}let B=se[U.id]??0,J=E.filter(Ee=>Ee.seq>B&&typeof Ee.id=="string"),Se=me==="http"?Be:Yr;for(let Ee=0;Ee<J.length;Ee+=Se){if(!await _R(U.id))return;let He=J.slice(Ee,Ee+Math.min(Se,me==="http"?Be:Yr)).map(Tt=>({trackId:U.id,event:Tt}));if(!Oa(He))return}}}function Ry(L){!m||!L?.trackId||typeof L.id!="string"||(hn.push({trackId:L.trackId,event:L}),wt||(wt=setTimeout(fp,UE),wt.unref?.()))}let Ma=new Map;async function MR(L){let{reqId:U,method:E,path:B,headers:J={},body:Se}=L,Ee=Object.fromEntries(Object.entries(J).filter(([,Tt])=>Tt!=null)),He=new AbortController;Ma.set(U,He);try{if(L.host){await NR(L,Ee,He);return}w&&(Ee["x-bostrat-token"]=w);let Tt=await fetch(h+B,{method:E,headers:Ee,body:Se?Buffer.from(Se,"base64"):void 0,signal:He.signal}),Ht=Object.fromEntries(Tt.headers);for(let Qe of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Ht[Qe];if(Ht["content-type"]||(Ht["content-type"]="application/octet-stream"),at({v:ct,ch:"control",type:"http_head",reqId:U,status:Tt.status,headers:Ht}),Tt.body){for await(let Qe of Tt.body)if(!at({v:ct,ch:"control",type:"http_data",reqId:U,data:Buffer.from(Qe).toString("base64")}))break}at({v:ct,ch:"control",type:"http_end",reqId:U,error:null})}finally{Ma.delete(U)}}function NR(L,U,E){let{reqId:B,method:J,path:Se,body:Ee}=L,He=new URL(h);return new Promise(Tt=>{let Ht=DU.request({host:He.hostname,port:He.port,method:J,path:Se,headers:{...U,host:L.host},signal:E.signal},Qe=>{let{connection:Cy,"keep-alive":N5,"transfer-encoding":P5,upgrade:D5,...PR}=Qe.headers;at({v:ct,ch:"control",type:"http_head",reqId:B,status:Qe.statusCode||502,headers:PR}),Qe.on("data",DR=>{at({v:ct,ch:"control",type:"http_data",reqId:B,data:Buffer.from(DR).toString("base64")})||Qe.destroy()}),Qe.on("end",()=>{at({v:ct,ch:"control",type:"http_end",reqId:B,error:null}),Tt()}),Qe.on("error",()=>{at({v:ct,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),Tt()})});Ht.on("error",Qe=>{at({v:ct,ch:"control",type:"http_end",reqId:B,error:Qe?.message||"view request failed"}),Tt()}),Ee&&Ht.write(Buffer.from(Ee,"base64")),Ht.end()})}function Oy(){if(v)return;b=new Qw(e,{headers:{authorization:`Bearer ${t}`}}),b.on("open",async()=>{let U=null;try{U=await s?.()||null}catch{U=null}at({v:ct,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:un,installKind:f,...typeof p=="function"?{updatePolicy:p()}:p?{updatePolicy:p}:{},providers:BU(),...U?{agents:U}:{}})}),b.on("message",U=>{let E;try{E=JSON.parse(U)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){m=!0,D=BE,le=null,N.log(`[plane-link] connected to plane as ${n}`);try{x?.()}catch{}E.minNodeVersion&&un&&kc(un,E.minNodeVersion)&&N.error(`[plane-link] this node is bostrat-node ${un}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${kd(f)}`);try{u?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let B=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(B&&un&&kc(un,B)&&!$.has(B)){$.add(B),N.log(`[plane-link] bostrat-node ${B} is available (this node runs ${un}) \u2014 ${kd(f)}`);try{l?.({current:un,latest:B,min:E.minNodeVersion??null,installKind:f,required:!!(E.minNodeVersion&&kc(un,E.minNodeVersion))})}catch(Se){N.error(`[plane-link] update notice failed: ${Se?.message||Se}`)}}let J=E.ingest==="http";me=y==="ws"?"ws":y==="http"||J?"http":"ws",y==="http"&&!J&&N.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),me==="http"&&N.log("[plane-link] spine rides the HTTPS ingest door"),F=!1,ge=0,Be=Yr,gt&&clearInterval(gt),gt=setInterval(()=>{at({v:ct,ch:"ctl",type:"ping"}),_y(),RR().catch(()=>{}),pp(),hn.length&&!wt&&fp()},P),gt.unref?.(),OR(),CR().catch(Se=>N.error("[plane-link] backfill failed:",Se?.message||Se));return}if(E.ch==="ctl"&&E.type==="hello_refused"){let B=typeof E.minNodeVersion=="string"?E.minNodeVersion:null,J=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;N.error(`[plane-link] plane REFUSED this node: bostrat-node ${un??"?"} is below its minimum ${B??"?"} \u2014 ${E.fix||kd(f)}`),D=LE,le=0;try{d?.({current:un,min:B,latest:J,fix:E.fix||kd(f),installKind:f})}catch(Se){N.error(`[plane-link] refusal handler failed: ${Se?.message||Se}`)}return}if(E.ch==="ctl"&&E.type==="update_policy"){try{u?.(typeof E.mode=="string"?E.mode:null)}catch{}return}if(E.ch==="ctl"&&E.type==="usage_caps"){ou(E.caps??{},{source:"plane",updatedAt:E.updatedAt??Date.now()}).then(B=>N.log(`[plane-link] spend caps ${Object.keys(B.caps).length?`updated (${Object.keys(B.caps).join(", ")})`:"cleared"} from plane`)).catch(B=>N.error("[plane-link] spend-caps push rejected:",B?.message||B));return}if(E.ch==="ctl"&&E.type==="transferred"&&Array.isArray(E.tracks)){Promise.resolve().then(()=>a?.(E.tracks)).catch(B=>N.error("[plane-link] transfer notice failed:",B?.message||B));return}if(E.ch==="events"&&E.type==="ack"&&Array.isArray(E.ids)){dp(E.ids);return}if(E.ch==="events"&&E.type==="nack"&&Array.isArray(E.ids)){Ay(E.ids,E.reason,E.permanent===!0,E.retryAfterMs??null);return}if(E.ch==="control"&&E.type==="http_open"){MR(E).catch(B=>at({v:ct,ch:"control",type:"http_end",reqId:E.reqId,error:B?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){Ma.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let B=new Qw(z+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);T.set(E.streamId,B),M.set(E.streamId,[]),B.on("open",()=>{let Se=M.get(E.streamId)||[];M.delete(E.streamId);for(let{buf:Ee,bin:He}of Se)B.send(Ee,{binary:He})}),B.on("message",(Se,Ee)=>{let He=Ee?Se:Buffer.from(String(Se));at({v:ct,ch:"attach",type:"data",streamId:E.streamId,data:He.toString("base64"),...Ee?{bin:!0}:{}})});let J=()=>{M.delete(E.streamId),T.delete(E.streamId)&&at({v:ct,ch:"attach",type:"close",streamId:E.streamId})};B.on("close",J),B.on("error",J);return}if(E.ch==="attach"&&E.type==="data"){let B=T.get(E.streamId);if(!B)return;let J=Buffer.from(E.data,"base64"),Se=E.bin===!0,Ee=M.get(E.streamId);Ee?Ee.push({buf:J,bin:Se}):B.send(J,{binary:Se});return}if(E.ch==="attach"&&E.type==="close"){let B=T.get(E.streamId);T.delete(E.streamId),M.delete(E.streamId),B?.close();return}});let L=()=>{m&&N.log("[plane-link] disconnected from plane"),m=!1,gt&&(clearInterval(gt),gt=null),Ce&&(clearInterval(Ce),Ce=null),ae.clear(),we={},hn=[],wt&&(clearTimeout(wt),wt=null),St.clear();for(let B of T.values())B.close();T.clear(),M.clear();for(let B of Ma.values())B.abort();if(Ma.clear(),v||ye)return;le==null&&(le=Date.now());let U=Date.now()-le<LU,E=Math.max(U?FU+Math.random()*UU:D,Y-Date.now());ye=setTimeout(()=>{ye=null,Oy()},E),ye.unref?.(),U||(D=Math.min(D*2,LE))};b.on("close",L),b.on("error",U=>{let E=/Unexpected server response: (\d+)/.exec(U?.message||"")?.[1];(E==="401"||E==="403")&&Date.now()-Jc>=HE&&(Jc=Date.now(),N.error(`[plane-link] plane refused this node's credentials (HTTP ${E}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),L()})}return rt.on("event",Ry),TR().then(Oy),{stop:()=>{v=!0,rt.off("event",Ry),ye&&clearTimeout(ye),Ue&&clearTimeout(Ue),gt&&clearInterval(gt),Ce&&clearInterval(Ce),wt&&clearTimeout(wt),at({v:ct,ch:"ctl",type:"bye",reason:"shutdown"}),b?.close()},status:()=>({connected:m,nodeId:n,cursor:{...se},inflight:ae.size,...Iy()}),_tick:()=>{_y(),pp()}}}Rl();var vd=(()=>{try{return ZU(import.meta.url)("../package.json").version||null}catch{return null}})(),QU=process.env.BOSTRAT_STATE_DIR||lt.join(process.env.HOME||JU.tmpdir(),".bostrat"),VE=lt.join(QU,"self-update"),Sd=lt.join(VE,"status.json"),e4=1440*6e4,t4=6e4,xc=new Set(["npm-global","git-checkout"]);function zE({kind:e=zo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:xc.has(e)?e==="git-checkout"||n4(t)?"auto":"prompt":"off"}function n4(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Bt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function r4({kind:e,to:t,from:n=vd,statusFile:r=Sd,logFile:o=lt.join(VE,"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||lt.resolve(lt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!xc.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(g,k="")=>`write_status ${Bt(g)} ${k}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Bt(i)}`,f=`
|
|
651
|
+
`);p4=new Set(["claude","codex","cursor"]);wc=new Set,f4=e=>wc.has(e);m4=9e4;BE={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};bw=new Map;Aw=120;eg=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:La(e.hookToken),createdAt:Date.now()});ya=new Map,xa=e=>ya.get(e)||(Vt?we(Vt,e)?.handoff:null)||null;Ew=6e4,ln=(e,t,n=15e3)=>HE("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),_w=new Map,ng=e=>_w.get(e)||null;k4=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;kw=new Map;S4=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,T4=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ba=re.join(jn,"worktrees"),da=[pc,fd],Pt=new Map,A4=e=>Pt.get(e)||(Vt?Vt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Zo=e=>{let t=new Map([...Pt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Vt?we(Vt,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},Id=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});Ec=e=>c_(e.trackId,e.id,Id(e));cg=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",yc=class extends Error{}});ct();import Te from"node:path";import wt from"node:fs";import AK from"node:readline";import{fileURLToPath as EK}from"node:url";import Ut from"express";import _K from"node:http";import IK from"node:https";import gp from"node:os";import{randomUUID as $K}from"node:crypto";import{WebSocketServer as RK}from"ws";ct();import PM from"node-pty";async function hk(e,t,n={}){if(!Be(t)){jl(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Er()){jl(e,`host tmux server not running on ${Rt}`),e.close(1011,"tmux server unavailable");return}if(!await ke(t)){jl(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=mk(n.cols,120,wk),o=mk(n.rows,32,gk),s;try{s=PM.spawn(X,["-S",Rt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){jl(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&nf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(nf(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&LM(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{}BM(t)})}var DM=6,jM=250;async function BM(e,t=DM,n=jM){for(let r=0;r<t;r++){if(await bo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var wk=20,gk=6;function LM(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=wk&&Number(t)>=gk}function mk(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function nf(e,t){try{e.send(JSON.stringify(t))}catch{}}function jl(e,t){nf(e,{type:"error",message:t})}function yk(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}nn();Ll();Kf();or();import{createRequire as OK}from"node:module";var P4=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function pg({tracks:e=[],views:t=[],queuedOf:n=()=>[],jobsOf:r=()=>[]}={}){let o=[];for(let s of e)!s||s.archivedAt&&!s.archiving||s.transferredTo||(s.working&&o.push(`working:${s.id}`),s.prompted&&o.push(`prompted:${s.id}`),(n(s.id)??[]).length&&o.push(`queued:${s.id}`),(s.boxes||[]).some(a=>a?.role==="subtask"&&a?.state==="live")&&o.push(`subtask:${s.id}`),(r(s.id)??[]).length&&o.push(`job:${s.id}`),Object.values(s.subtasks||{}).some(P4)&&o.push(`merge-back:${s.id}`));return t.some(s=>s?.state==="live"||s?.state==="starting")&&o.push("views"),{busy:o.length>0,reasons:o}}de();sm();ct();import L4 from"node:fs/promises";import fg from"node:fs/promises";var D4=100;async function j4(e){let t;try{t=await fg.readFile(`/proc/${e}/stat`,"utf8")}catch{return null}let n=t.lastIndexOf(")");if(n<0)return null;let r=t.slice(n+2).split(" "),o=Number(r[1]),s=Number(r[11])+Number(r[12]);return!Number.isFinite(o)||!Number.isFinite(s)?null:{ppid:o,cpuMs:s/D4*1e3,ioBytes:await B4(e)}}async function B4(e){let t;try{t=await fg.readFile(`/proc/${e}/io`,"utf8")}catch{return 0}let n=r=>Number(t.match(new RegExp(`^${r}: (\\d+)$`,"m"))?.[1]||0);return n("rchar")+n("wchar")}async function m_(e){let t=[...new Set((e||[]).map(Number).filter(l=>Number.isFinite(l)&&l>0))];if(!t.length)return{procs:0,cpuMs:0,ioBytes:0};let n;try{n=await fg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await j4(l);u&&r.set(Number(l),u)}));let o=new Map;for(let[l,u]of r)o.has(u.ppid)||o.set(u.ppid,[]),o.get(u.ppid).push(l);let s=new Set,a=t.filter(l=>r.has(l));for(let l of a)s.add(l);let i=0,c=0;for(let l=0;l<a.length;l++){let u=a[l];i+=r.get(u).cpuMs,c+=r.get(u).ioBytes;for(let d of o.get(u)||[])s.has(d)||(s.add(d),a.push(d))}return{procs:s.size,cpuMs:Math.round(i),ioBytes:c}}var F4=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,U4=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,H4=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,W4=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,q4=5*6e4,w_={readOpenJobs:Lx,statFile:async e=>{try{let t=await L4.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>jr(e.agent).updatedAt(e),panePids:dk,treeStats:m_,appendEvent:C},un=new Map,h_=new Map,mg=new Map,hg=(e,t,n)=>`${e}|${t}|${n}`;function g_(e){let t=[];for(let n of e||[])if(!(!n||n.archivedAt&&!n.archiving||n.transferredTo))for(let r of n.boxes||[])r?.state==="live"&&r.name&&t.push({track:n,box:r});return t}async function y_(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=h_.get(e.name);if(o&&o.mtime===r&&t-o.at<q4)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return h_.set(e.name,{at:t,mtime:r,jobs:s}),s}async function K4(e,t,n){let r=await n.panePids(e.name),o=r.length?await n.treeStats(r):null,s=null;try{s=await n.updatedAt(e)}catch{s=null}return{at:t,cpuMs:o?.cpuMs??null,ioBytes:o?.ioBytes??null,procs:o?.procs??null,mtime:s}}async function G4(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function z4(e,t){if(!e)return!0;let n=Math.max(1,(t.at-e.at)/1e3);return t.mtime!=null&&e.mtime!=null&&t.mtime>e.mtime||t.procs!=null&&e.procs!=null&&t.procs>e.procs||t.cpuMs!=null&&e.cpuMs!=null&&(t.cpuMs-e.cpuMs)/n>=H4||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=W4}function b_(e,t,n,r){let o=[];for(let s of n){let a=hg(e.id,t.name,s.id),i=un.get(a);i?i.job=s:un.set(a,{trackId:e.id,boxName:t.name,agent:t.agent||null,job:s,firstSeenAt:r,quiet:0,stalled:!1}),o.push(a)}return o}async function k_(e,{now:t=Date.now(),deps:n=w_}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of g_(e)){let i=await y_(a,t,n);for(let l of i)o.add(hg(s.id,a.name,l.id));let c=b_(s,a,i,t).filter(l=>!un.get(l).stalled).map(l=>un.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...un.keys()])o.has(s)||un.delete(s);return r}async function V4(e,{now:t=Date.now(),deps:n=w_}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=g_(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...un])s.has(i.boxName)||un.delete(a);for(let{track:a,box:i}of o){let c=await y_(i,t,n),l=new Set(c.map(w=>hg(a.id,i.name,w.id)));for(let[w,m]of[...un])m.boxName===i.name&&!l.has(w)&&un.delete(w);if(!c.length){mg.delete(i.name);continue}let u=b_(a,i,c,t),d=await K4(i,t,n),p=mg.get(i.name);mg.set(i.name,d);let f=z4(p,d);for(let w of u){let m=un.get(w);r.checked++;let g=await G4(m.job,n),b=!!g&&!!m.file&&(g.size>m.file.size||g.mtimeMs>m.file.mtimeMs),x=!!g&&!m.file;if(m.file=g??m.file,f||b||x){m.quiet=0,m.lastProgressAt=t,m.stalled=!1,r.progressing++;continue}if(m.stalled||(m.quiet++,r.quiet++,m.quiet<U4))continue;m.stalled=!0,m.stalledAt=t,r.stalled.push(w);let $=t-(m.lastProgressAt??m.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:m.job.id,kind:m.job.kind||"agent",name:m.job.name||null,detail:m.job.detail||null,startedTs:m.job.ts??m.firstSeenAt,quietMs:$,checks:m.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${m.job.id} (${m.job.name||m.job.kind}) quiet for ${Math.round($/6e4)}m over ${m.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function x_({listTracks:e,intervalMs:t=F4}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await V4(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}import M_ from"node:fs";import no from"node:fs/promises";import d2 from"node:os";import dt from"node:path";import{spawn as p2}from"node:child_process";import{createRequire as f2}from"node:module";de();hu();import R_ from"node:fs";import Ic from"node:fs/promises";import X4 from"node:http";import pn from"node:path";import Od from"node:os";import{createRequire as Z4}from"node:module";import gg from"ws";import J4 from"node:path";import Y4 from"node:os";var _c=process.env.BOSTRAT_STATE_DIR||J4.join(process.env.HOME||Y4.tmpdir(),".bostrat");function wg(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 v_(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=wg(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function S_(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 dn=(()=>{try{return Z4(import.meta.url)("../package.json").version||null}catch{return null}})(),T_=process.env.BOSTRAT_STATE_DIR||pn.join(process.env.HOME||Od.tmpdir(),".bostrat");function Qo({env:e=process.env,pkgDir:t=pn.resolve(pn.dirname(new URL(import.meta.url).pathname),".."),exists:n=R_.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(pn.join(t,"..",".git"))?"git-checkout":"dev"}function $c(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 Rd(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 Q4({home:e=process.env.HOME||Od.homedir(),env:t=process.env}={}){let n=r=>{try{return R_.statSync(r),!0}catch{return!1}};return{claude:n(pn.join(e,".claude",".credentials.json")),codex:n(pn.join(t.CODEX_HOME||pn.join(e,".codex"),"auth.json"))}}var ut=1,A_=1e3,E_=3e4,e2=6e4,t2=1500,n2=500,to=100,r2=2e3,o2=25e3,s2=6e4,O_=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},a2=O_(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),i2=3e4,__=10*6e4,c2=400,I_=250,l2=O_(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),yg=4,u2=6e4,$_=6e4;function C_({url:e,token:t,nodeId:n=Od.hostname(),name:r=Od.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,metricsMs:u=u2,onUpdateAvailable:d=null,onUpdatePolicy:p=null,onHelloRefused:f=null,updatePolicy:w=null,installKind:m=Qo(),selfUrl:g=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:b=null,onDegraded:x=null,onRecovered:y=null,onConnected:$=null,ingestMode:O=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:M=a2,unackedRetryMs:H=s2,pingMs:P=o2,maxInflightEvents:E=c2,httpTimeoutMs:R=l2,stallBackoffMinMs:G=i2,fetchImpl:V=globalThis.fetch,log:A=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=S_(e);let _=g.replace(/^http/,"ws"),N=new Set,U=new Map,z=new Map,ue=pn.join(T_,"plane-link","cursor.json"),pe=pn.join(T_,"plane-link","dead-letter.ndjson"),Se=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),Ae={},W={},te=new Map,ie=null,J=null,ge=0,Oe=0,Me=0,I=Promise.resolve(),j="ws",fe=0,Pe=!1,Ce=0,wn=!1,gt=to,gr=new Set,gn=[],k=null,h=null,v=!1,D=!1,ce=A_,ye=null,De=null,St=null,qe=null,rt=null,kp=!1,sl=0;async function pO(){try{Ae=JSON.parse(await Ic.readFile(ue,"utf8"))}catch{Ae={}}}function fO(){kp=!0,!St&&(St=setTimeout(async()=>{if(St=null,!!kp){kp=!1;try{await Ic.mkdir(pn.dirname(ue),{recursive:!0}),await Ic.writeFile(ue,JSON.stringify(Ae))}catch(L){A.error("[plane-link] cursor flush failed:",L?.message||L)}}},r2),St.unref?.())}function it(L){return!h||h.readyState!==gg.OPEN?!1:(h.send(JSON.stringify(L)),!0)}function Da(L){let F=Date.now();for(let{trackId:T,event:B}of L){let Y=te.get(B.id);te.set(B.id,{trackId:T,seq:B.seq,event:B,sentAt:F,firstSentAt:Y?.firstSentAt??F,retryAt:F+H,tries:(Y?.tries??0)+1,nacks:Y?.nacks??0})}return j==="http"?(gO(L),!0):it({v:ut,ch:"events",type:"append",events:L})}function mO(L){let F=null;for(let T of te.values())T.trackId===L&&(F==null||T.seq<F)&&(F=T.seq);return F}function xp(L){let F=W[L];if(F==null)return;let T=mO(L),B=T==null?F:Math.min(F,T-1);B>(Ae[L]??0)&&(Ae[L]=B,fO())}function ja(){if(ie=Date.now(),J!=null){let L=Date.now()-J;A.log(`[plane-link] spine sync recovered after ${Math.round(L/1e3)}s degraded (${ge} stall redial(s))`);let F=J;J=null,ge=0,Oe=0,Pe=!1,Ce=0;try{y?.({since:F,downMs:L})}catch(T){A.error(`[plane-link] onRecovered failed: ${T?.message||T}`)}}}function vp(L){if(!Array.isArray(L)||!L.length)return;let F=new Set;for(let T of L){let B=te.get(T);B&&(te.delete(T),(W[B.trackId]??0)<B.seq&&(W[B.trackId]=B.seq),F.add(B.trackId))}for(let T of F)xp(T);ja()}function Hy(L,F){Me++;let T={at:Date.now(),trackId:L.trackId,seq:L.seq,id:L.event?.id??null,type:L.event?.type??null,reason:F,event:L.event??null};A.error(`[plane-link] DEAD-LETTER ${L.trackId} seq ${L.seq} (${T.type??"?"}): plane rejected it permanently (${F}) \u2014 kept in ${pe}`),I=I.then(async()=>{await Ic.mkdir(pn.dirname(pe),{recursive:!0}),await Ic.appendFile(pe,JSON.stringify(T)+`
|
|
652
|
+
`)}).catch(B=>A.error(`[plane-link] dead-letter write failed: ${B?.message||B}`))}function Wy(L,F,T,B=null){if(!Array.isArray(L)||!L.length)return;let Y=new Set,xe=Date.now();for(let Ee of L){let Ue=te.get(Ee);Ue&&(T?(te.delete(Ee),Hy(Ue,F),(W[Ue.trackId]??0)<Ue.seq&&(W[Ue.trackId]=Ue.seq),Y.add(Ue.trackId)):(Ue.nacks++,Ue.retryAt=xe+Math.max(1e3,Number(B)||H)))}for(let Ee of Y)xp(Ee);ja()}function qy(L=Date.now()){let F=1/0;for(let T of te.values())T.firstSentAt<F&&(F=T.firstSentAt);return F===1/0?0:L-F}function Ky(){if(!v||!te.size)return;let L=Date.now(),F=qy(L);if(F<M||ie!=null&&L-ie<M||j==="http"&&Pe||Oe>L)return;ge++;let T=Math.min(G*2**(ge-1),__);if(J==null){J=L-F,A.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(F/1e3)}s with ${te.size} event(s) in flight (${j} door) \u2014 the plane is not answering appends`);try{x?.({since:J,unacked:te.size,oldestAgeMs:F,mode:j})}catch(B){A.error(`[plane-link] onDegraded failed: ${B?.message||B}`)}}if(j==="http"){Pe=!0,Ce=L+T,A.error(`[plane-link] ingest circuit OPEN (stall #${ge}); probing /v1/ingest/health in ${Math.round(T/1e3)}s`);return}Oe=L+T,A.error(`[plane-link] closing the link and redialing in ${Math.round(T/1e3)}s (stall redial #${ge})`);try{h?.terminate()}catch{}}function Sp(){if(!v)return;let L=Date.now();if(j==="http"&&Pe)return;let F=[...te.values()].filter(B=>B.event&&B.retryAt<=L&&!(j==="http"&&gr.has(B.trackId)));if(!F.length)return;F.sort((B,Y)=>B.trackId===Y.trackId?B.seq-Y.seq:B.trackId<Y.trackId?-1:1),A.log(`[plane-link] re-sending ${F.length} unacked event(s) the plane never confirmed`);let T=j==="http"?gt:to;if(j==="http"){let B=new Map;for(let Y of F)(B.get(Y.trackId)??B.set(Y.trackId,[]).get(Y.trackId)).push(Y);for(let[,Y]of B){if(fe>=yg)return;Da(Y.slice(0,T).map(xe=>({trackId:xe.trackId,event:xe.event})))}return}for(let B=0;B<F.length;B+=T){let Y=F.slice(B,B+T).map(xe=>({trackId:xe.trackId,event:xe.event}));if(!Da(Y))return}}async function hO(L=null){for(;;){if(!v)return!1;let F=te.size<E,T=j!=="http"||fe<yg&&!Pe&&(!L||!gr.has(L));if(F&&T)return!0;await new Promise(B=>setTimeout(B,100))}}function Tp(){if(k=null,!v||!gn.length)return;let L=gn;if(gn=[],j!=="http"){for(let T=0;T<L.length;T+=to)Da(L.slice(T,T+to));return}let F=new Map;for(let T of L)(F.get(T.trackId)??F.set(T.trackId,[]).get(T.trackId)).push(T);for(let[T,B]of F){if(gr.has(T)||fe>=yg||Pe){gn.push(...B);continue}for(let Y=0;Y<B.length;Y+=gt)Da(B.slice(Y,Y+gt))}gn.length&&!k&&(k=setTimeout(Tp,I_),k.unref?.())}let wO=L=>{let F=new Map;for(let{trackId:T,event:B}of L)(F.get(T)??F.set(T,[]).get(T)).push(B);return[...F].map(([T,B])=>({trackId:T,events:B}))},us=(L,F,T,{progress:B})=>{let Y=Date.now();for(let{event:xe}of L){let Ee=te.get(xe.id);Ee&&(Ee.nacks++,Ee.retryAt=Y+Math.max(1e3,T||H))}B&&ja()};async function gO(L){let F=new Set(L.map(T=>T.trackId));for(let T of F)gr.add(T);fe++;try{let T=await V(`${Se}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:wO(L)}),signal:AbortSignal.timeout(R)}),B=(Number(T.headers.get("retry-after"))||0)*1e3;if(T.status===200){let Y=await T.json();gt=to;for(let xe of Y.results??[]){vp(xe.acked??[]);let Ee=(Ue,Tt)=>{let Ht=new Map;for(let Qe of Ue??[])(Ht.get(Qe.reason)??Ht.set(Qe.reason,[]).get(Qe.reason)).push(Qe.id);for(let[Qe,Yy]of Ht)Wy(Yy,Qe,Tt,Y.retryAfterMs??null)};Ee(xe.rejected,!0),Ee(xe.deferred,!1)}vp(Y.discarded??[]),ja();return}if(T.status===429){us(L,"overloaded",B||5e3,{progress:!0});return}if(T.status===413&&L.length>1){gt=Math.max(1,Math.floor(L.length/2)),A.error(`[plane-link] ingest request too large (${L.length} events) \u2014 halving batches to ${gt}`),us(L,"too_large",1e3,{progress:!0});return}if(T.status===413){let Y=te.get(L[0].event.id);Y&&(te.delete(Y.event.id),Hy(Y,"too_large"),(W[Y.trackId]??0)<Y.seq&&(W[Y.trackId]=Y.seq),xp(Y.trackId)),ja();return}if(T.status===401||T.status===403){if(Date.now()-sl>=$_){sl=Date.now();let Y="";try{Y=(await T.json())?.error??""}catch{}A.error(`[plane-link] plane refused ingest (HTTP ${T.status}${Y?`: ${Y}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}us(L,`http_${T.status}`,6e4,{progress:!1});return}if(T.status===400){A.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${L.length} event(s) for retry; this is a bug, not data`),us(L,"http_400",6e4,{progress:!1});return}us(L,`http_${T.status}`,B||5e3,{progress:!1})}catch(T){let B=T?.name==="TimeoutError"||T?.name==="AbortError";B&&L.length>1&&(gt=Math.max(1,Math.floor(L.length/2)),A.error(`[plane-link] ingest request of ${L.length} event(s) outlived its ${R} ms deadline \u2014 halving batches to ${gt}`)),us(L,B?"timeout":"network",5e3,{progress:!1})}finally{fe--;for(let T of F)gr.delete(T)}}async function yO(){if(j!=="http"||!Pe||!v||wn)return;let L=Date.now();if(!(Ce>L)){wn=!0;try{let F=await V(`${Se}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(R)});if(!F.ok)throw new Error(`HTTP ${F.status}`);A.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),Pe=!1,Ce=0,Oe=Date.now()+R+H+1e3;for(let T of te.values())T.retryAt=Math.min(T.retryAt,Date.now());Sp()}catch(F){ge++;let T=Math.min(G*2**(ge-1),__);Ce=Date.now()+T,A.error(`[plane-link] ingest door still not answering (${F?.message||F}); next probe in ${Math.round(T/1e3)}s`)}finally{wn=!1}}}function Gy(){let L=Date.now();return{mode:j,unacked:te.size,oldestUnackedAgeMs:qy(L),lastProgressAt:ie,degradedSince:J,stallRedials:ge,deadLettered:Me,http:j==="http"?{inFlight:fe,circuitOpen:Pe,nextProbeInMs:Pe?Math.max(0,Ce-L):0,batchCap:gt}:null}}async function zy(){if(!i)return;let L;try{L=await i()}catch(T){A.error("[plane-link] metrics sample failed:",T?.message||T);return}let F=null;try{F=c?.()??null}catch{}L&&it({v:ut,ch:"ctl",type:"metrics",metrics:{...L,planeLink:Gy(),...F?{delivery:F}:{}}})}function bO(){i&&(rt&&clearInterval(rt),zy(),rt=setInterval(zy,u),rt.unref?.())}async function kO(){let L;try{L=await o()}catch(F){A.error("[plane-link] backfill listTracks failed:",F?.message||F);return}it({v:ut,ch:"ctl",type:"tracks",ids:L.filter(F=>!F.transferredTo).map(F=>F.id).filter(F=>typeof F=="string")});for(let F of L){if(!v)return;let T;try{T=await K(F)}catch{continue}let B=Ae[F.id]??0,Y=T.filter(Ee=>Ee.seq>B&&typeof Ee.id=="string"),xe=j==="http"?gt:to;for(let Ee=0;Ee<Y.length;Ee+=xe){if(!await hO(F.id))return;let Ue=Y.slice(Ee,Ee+Math.min(xe,j==="http"?gt:to)).map(Tt=>({trackId:F.id,event:Tt}));if(!Da(Ue))return}}}function Vy(L){!v||!L?.trackId||typeof L.id!="string"||(gn.push({trackId:L.trackId,event:L}),k||(k=setTimeout(Tp,I_),k.unref?.()))}let Ba=new Map;async function xO(L){let{reqId:F,method:T,path:B,headers:Y={},body:xe}=L,Ee=Object.fromEntries(Object.entries(Y).filter(([,Tt])=>Tt!=null)),Ue=new AbortController;Ba.set(F,Ue);try{if(L.host){await vO(L,Ee,Ue);return}b&&(Ee["x-bostrat-token"]=b);let Tt=await fetch(g+B,{method:T,headers:Ee,body:xe?Buffer.from(xe,"base64"):void 0,signal:Ue.signal}),Ht=Object.fromEntries(Tt.headers);for(let Qe of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Ht[Qe];if(Ht["content-type"]||(Ht["content-type"]="application/octet-stream"),it({v:ut,ch:"control",type:"http_head",reqId:F,status:Tt.status,headers:Ht}),Tt.body){for await(let Qe of Tt.body)if(!it({v:ut,ch:"control",type:"http_data",reqId:F,data:Buffer.from(Qe).toString("base64")}))break}it({v:ut,ch:"control",type:"http_end",reqId:F,error:null})}finally{Ba.delete(F)}}function vO(L,F,T){let{reqId:B,method:Y,path:xe,body:Ee}=L,Ue=new URL(g);return new Promise(Tt=>{let Ht=X4.request({host:Ue.hostname,port:Ue.port,method:Y,path:xe,headers:{...F,host:L.host},signal:T.signal},Qe=>{let{connection:Yy,"keep-alive":JK,"transfer-encoding":YK,upgrade:XK,...SO}=Qe.headers;it({v:ut,ch:"control",type:"http_head",reqId:B,status:Qe.statusCode||502,headers:SO}),Qe.on("data",TO=>{it({v:ut,ch:"control",type:"http_data",reqId:B,data:Buffer.from(TO).toString("base64")})||Qe.destroy()}),Qe.on("end",()=>{it({v:ut,ch:"control",type:"http_end",reqId:B,error:null}),Tt()}),Qe.on("error",()=>{it({v:ut,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),Tt()})});Ht.on("error",Qe=>{it({v:ut,ch:"control",type:"http_end",reqId:B,error:Qe?.message||"view request failed"}),Tt()}),Ee&&Ht.write(Buffer.from(Ee,"base64")),Ht.end()})}function Jy(){if(D)return;h=new gg(e,{headers:{authorization:`Bearer ${t}`}}),h.on("open",async()=>{let F=null;try{F=await s?.()||null}catch{F=null}it({v:ut,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:dn,installKind:m,...typeof w=="function"?{updatePolicy:w()}:w?{updatePolicy:w}:{},providers:Q4(),...F?{agents:F}:{}})}),h.on("message",F=>{let T;try{T=JSON.parse(F)}catch{return}if(T.ch==="ctl"&&T.type==="hello_ok"){v=!0,ce=A_,ye=null,A.log(`[plane-link] connected to plane as ${n}`);try{$?.()}catch{}if(typeof T.hashKey=="string"&&T.hashKey)try{l?.(T.hashKey)}catch{}T.minNodeVersion&&dn&&$c(dn,T.minNodeVersion)&&A.error(`[plane-link] this node is bostrat-node ${dn}; the plane asks for \u2265${T.minNodeVersion} \u2014 update with: ${Rd(m)}`);try{p?.(typeof T.updatePolicy=="string"?T.updatePolicy:null)}catch{}let B=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;if(B&&dn&&$c(dn,B)&&!N.has(B)){N.add(B),A.log(`[plane-link] bostrat-node ${B} is available (this node runs ${dn}) \u2014 ${Rd(m)}`);try{d?.({current:dn,latest:B,min:T.minNodeVersion??null,installKind:m,required:!!(T.minNodeVersion&&$c(dn,T.minNodeVersion))})}catch(xe){A.error(`[plane-link] update notice failed: ${xe?.message||xe}`)}}let Y=T.ingest==="http";j=O==="ws"?"ws":O==="http"||Y?"http":"ws",O==="http"&&!Y&&A.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),j==="http"&&A.log("[plane-link] spine rides the HTTPS ingest door"),Pe=!1,Ce=0,gt=to,qe&&clearInterval(qe),qe=setInterval(()=>{it({v:ut,ch:"ctl",type:"ping"}),Ky(),yO().catch(()=>{}),Sp(),gn.length&&!k&&Tp()},P),qe.unref?.(),bO(),kO().catch(xe=>A.error("[plane-link] backfill failed:",xe?.message||xe));return}if(T.ch==="ctl"&&T.type==="hello_refused"){let B=typeof T.minNodeVersion=="string"?T.minNodeVersion:null,Y=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;A.error(`[plane-link] plane REFUSED this node: bostrat-node ${dn??"?"} is below its minimum ${B??"?"} \u2014 ${T.fix||Rd(m)}`),ce=E_,ye=0;try{f?.({current:dn,min:B,latest:Y,fix:T.fix||Rd(m),installKind:m})}catch(xe){A.error(`[plane-link] refusal handler failed: ${xe?.message||xe}`)}return}if(T.ch==="ctl"&&T.type==="update_policy"){try{p?.(typeof T.mode=="string"?T.mode:null)}catch{}return}if(T.ch==="ctl"&&T.type==="usage_caps"){mu(T.caps??{},{source:"plane",updatedAt:T.updatedAt??Date.now()}).then(B=>A.log(`[plane-link] spend caps ${Object.keys(B.caps).length?`updated (${Object.keys(B.caps).join(", ")})`:"cleared"} from plane`)).catch(B=>A.error("[plane-link] spend-caps push rejected:",B?.message||B));return}if(T.ch==="ctl"&&T.type==="transferred"&&Array.isArray(T.tracks)){Promise.resolve().then(()=>a?.(T.tracks)).catch(B=>A.error("[plane-link] transfer notice failed:",B?.message||B));return}if(T.ch==="events"&&T.type==="ack"&&Array.isArray(T.ids)){vp(T.ids);return}if(T.ch==="events"&&T.type==="nack"&&Array.isArray(T.ids)){Wy(T.ids,T.reason,T.permanent===!0,T.retryAfterMs??null);return}if(T.ch==="control"&&T.type==="http_open"){xO(T).catch(B=>it({v:ut,ch:"control",type:"http_end",reqId:T.reqId,error:B?.message||"tunnel error"}));return}if(T.ch==="control"&&T.type==="http_cancel"){Ba.get(T.reqId)?.abort();return}if(T.ch==="attach"&&T.type==="open"){let B=new gg(_+T.path,Array.isArray(T.protocols)?T.protocols:[],T.host?{headers:{host:T.host}}:void 0);U.set(T.streamId,B),z.set(T.streamId,[]),B.on("open",()=>{let xe=z.get(T.streamId)||[];z.delete(T.streamId);for(let{buf:Ee,bin:Ue}of xe)B.send(Ee,{binary:Ue})}),B.on("message",(xe,Ee)=>{let Ue=Ee?xe:Buffer.from(String(xe));it({v:ut,ch:"attach",type:"data",streamId:T.streamId,data:Ue.toString("base64"),...Ee?{bin:!0}:{}})});let Y=()=>{z.delete(T.streamId),U.delete(T.streamId)&&it({v:ut,ch:"attach",type:"close",streamId:T.streamId})};B.on("close",Y),B.on("error",Y);return}if(T.ch==="attach"&&T.type==="data"){let B=U.get(T.streamId);if(!B)return;let Y=Buffer.from(T.data,"base64"),xe=T.bin===!0,Ee=z.get(T.streamId);Ee?Ee.push({buf:Y,bin:xe}):B.send(Y,{binary:xe});return}if(T.ch==="attach"&&T.type==="close"){let B=U.get(T.streamId);U.delete(T.streamId),z.delete(T.streamId),B?.close();return}});let L=()=>{v&&A.log("[plane-link] disconnected from plane"),v=!1,qe&&(clearInterval(qe),qe=null),rt&&(clearInterval(rt),rt=null),te.clear(),W={},gn=[],k&&(clearTimeout(k),k=null),gr.clear();for(let B of U.values())B.close();U.clear(),z.clear();for(let B of Ba.values())B.abort();if(Ba.clear(),D||De)return;ye==null&&(ye=Date.now());let F=Date.now()-ye<e2,T=Math.max(F?t2+Math.random()*n2:ce,Oe-Date.now());De=setTimeout(()=>{De=null,Jy()},T),De.unref?.(),F||(ce=Math.min(ce*2,E_))};h.on("close",L),h.on("error",F=>{let T=/Unexpected server response: (\d+)/.exec(F?.message||"")?.[1];(T==="401"||T==="403")&&Date.now()-sl>=$_&&(sl=Date.now(),A.error(`[plane-link] plane refused this node's credentials (HTTP ${T}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),L()})}return ot.on("event",Vy),pO().then(Jy),{stop:()=>{D=!0,ot.off("event",Vy),De&&clearTimeout(De),St&&clearTimeout(St),qe&&clearInterval(qe),rt&&clearInterval(rt),k&&clearTimeout(k),it({v:ut,ch:"ctl",type:"bye",reason:"shutdown"}),h?.close()},status:()=>({connected:v,nodeId:n,cursor:{...Ae},inflight:te.size,...Gy()}),_tick:()=>{Ky(),Sp()}}}Fl();var Cd=(()=>{try{return f2(import.meta.url)("../package.json").version||null}catch{return null}})(),m2=process.env.BOSTRAT_STATE_DIR||dt.join(process.env.HOME||d2.tmpdir(),".bostrat"),N_=dt.join(m2,"self-update"),Md=dt.join(N_,"status.json"),h2=1440*6e4,w2=6e4,Rc=new Set(["npm-global","git-checkout"]);function P_({kind:e=Qo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Rc.has(e)?e==="git-checkout"||g2(t)?"auto":"prompt":"off"}function g2(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Bt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function y2({kind:e,to:t,from:n=Cd,statusFile:r=Md,logFile:o=dt.join(N_,"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||dt.resolve(dt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Rc.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(g,b="")=>`write_status ${Bt(g)} ${b}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Bt(i)}`,f=`
|
|
649
653
|
health() {
|
|
650
654
|
for i in $(seq 1 45); do
|
|
651
655
|
sleep 1
|
|
652
656
|
if curl -sf -o /dev/null "http://127.0.0.1:${s}/api/health"; then return 0; fi
|
|
653
657
|
done
|
|
654
658
|
return 1
|
|
655
|
-
}`,
|
|
659
|
+
}`,w=`#!/usr/bin/env bash
|
|
656
660
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
657
661
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
658
662
|
set -u
|
|
@@ -668,8 +672,8 @@ write_status() { # <status> [error]
|
|
|
668
672
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
669
673
|
}
|
|
670
674
|
${f}
|
|
671
|
-
`;if(e==="npm-global"){let g=a?
|
|
672
|
-
`:"",x=a?` --prefix ${Bt(a)}`:"",y
|
|
675
|
+
`;if(e==="npm-global"){let g=a?dt.join(a,"bin","npm"):"npm",b=a?`export PATH=${Bt(dt.join(a,"bin"))}:"$PATH"
|
|
676
|
+
`:"",x=a?` --prefix ${Bt(a)}`:"",y=$=>`${Bt(g)} install -g "bostrat-node@${$}" --no-audit --no-fund${x}`;return w+b+`
|
|
673
677
|
${d("updating",'"" ')}
|
|
674
678
|
if ! ${y("$TO")}; then
|
|
675
679
|
${d("failed",'"npm install failed"')}
|
|
@@ -692,7 +696,7 @@ if [ -n "$FROM" ] && ${y("$FROM")} && ${p} && health; then
|
|
|
692
696
|
fi
|
|
693
697
|
${d("failed",'"agent did not answer after the update AND the rollback"')}
|
|
694
698
|
exit 1
|
|
695
|
-
`}let
|
|
699
|
+
`}let m=g=>`( cd ${Bt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return w+`
|
|
696
700
|
LIVE=${Bt(l)}
|
|
697
701
|
TMP_SCRIPT="$(mktemp)"
|
|
698
702
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
@@ -700,75 +704,75 @@ trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
|
700
704
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
701
705
|
# and nothing here may write after that boot or the settle would repeat.
|
|
702
706
|
${d("restarting")}
|
|
703
|
-
if ${
|
|
707
|
+
if ${m("$TO")}; then
|
|
704
708
|
exit 0
|
|
705
709
|
fi
|
|
706
710
|
${d("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
707
|
-
if [ -n "$FROM" ] && ${
|
|
711
|
+
if [ -n "$FROM" ] && ${m("$FROM")}; then
|
|
708
712
|
${d("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
709
713
|
exit 1
|
|
710
714
|
fi
|
|
711
715
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
712
716
|
exit 1
|
|
713
|
-
`}async function vc(e=Sd){try{return JSON.parse(await Jr.readFile(e,"utf8"))}catch{return null}}var o4=new Set(["updating","restarting","rolling-back"]),Td=e=>!!(e&&o4.has(e.status));async function Ad({to:e,from:t=vd,kind:n=zo(),reason:r="manual",statusFile:o=Sd,spawnImpl:s=XU,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!xc.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!kc(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await vc(o);if(Td(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 Jr.mkdir(lt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?uf(await s4())||lt.resolve(lt.dirname(process.execPath),".."):null,d=r4({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=lt.join(lt.dirname(o),`update-${e}.sh`),f=lt.join(lt.dirname(o),"last.log");await Jr.writeFile(p,d,{mode:448}),await Jr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let h=KE.existsSync("/run/systemd/system")&&await a4("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Bt(p)} >> ${Bt(f)} 2>&1`]];try{s(h[0],h[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await Jr.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:p,statusFile:o}}async function s4(){try{return await Jr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function a4(e){for(let t of String(process.env.PATH||"").split(lt.delimiter))try{return await Jr.access(lt.join(t,e),KE.constants.X_OK),!0}catch{}return!1}var i4=45*6e4;async function YE({statusFile:e=Sd,current:t=vd,now:n=Date.now,staleMs:r=i4}={}){let o=await vc(e);if(!o)return null;let s=a=>Jr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Td(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function JE({isBusy:e,start:t=Ad,onStuck:n=null,onStarted:r=null,tickMs:o=t4,stuckMs:s=e4,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let h=u;d();let w=await t({to:h.to,from:h.from,kind:h.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...h,result:w})}catch{}};return{arm({to:f,from:h=vd,kind:w=zo()}){return u?.to===f?!1:(d(),u={to:f,from:h,kind:w,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}fe();ut();var XE=(...e)=>fetch(...e);function ZE(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function tg(e=process.env){if(!ZE(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 XE(`${Re(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function ng(e=process.env){if(!ZE(e))throw new Error("no plane configured \u2014 the roadmap lives 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 XE(`${Re(e)}/v1/roadmap`,{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{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function wa(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await tg(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 Ed(e,t,{source:n="chat"}={}){return C(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}fe();ut();var c4=(...e)=>fetch(...e);async function _d(e,t,n,r=process.env){if(!(Re(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await c4(`${Re(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function l4(e,t=process.env){return _d("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function QE({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await l4(t,o):await wa(e,o),a=await _d("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function rg(e,t=process.env){let n=await wa(e,t),r=await _d("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function og(e,t=process.env){return(await _d("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function e_(e,t,n,r=process.env){let{blueprint:o,items:s}=await rg(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 C(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}fe();ut();var u4=(...e)=>fetch(...e);async function d4(e,t,n,r=process.env){if(!(Re(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await u4(`${Re(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var t_=72,sg=160;async function n_(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>t_)throw new Error(`the title is the choice as a LABEL \u2014 ${t_} 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 ${sg} chars) a reader can act on without opening the record`);if(c.length>sg)throw new Error(`the summary is one sentence \u2014 ${sg} 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 C(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 r_(e,t=process.env){return d4("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}qh();fe();import Ic from"node:fs/promises";import Dd from"node:path";import n2 from"node:os";gr();import p4 from"node:fs/promises";import o_ from"node:path";import f4 from"node:os";var s_=()=>o_.join(process.env.BOSTRAT_STATE_DIR||o_.join(process.env.HOME||f4.tmpdir(),".bostrat"),"integrations.json");async function dr(){try{return JSON.parse(await p4.readFile(s_(),"utf8"))||{}}catch{return{}}}var Id=["linked","prOpened","prMerged","create"],ag=["prompt","auto","off"],m4={create:["prompt","off"]},a_=e=>m4[e]??ag;function $d(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Id){let s=a_(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 Yo(){let e=await dr(),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:$d(e),defaultTeam:n}:null}async function Rd(){let e=await dr(),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:$d(e)}}async function ig(){return $d(await dr())}async function i_(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Id.join(", ")}`);for(let[r,o]of t){if(!Id.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Id.join(", ")}`);let s=a_(r);if(!s.includes(o))throw new Error(s.length<ag.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${ag.join(", ")}`)}let n=await dr();return n.ticketFlow={...$d(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Et(s_(),n,{mode:384}),n.ticketFlow}async function Sc(){let e=(await dr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function h4(){let e=(await dr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var w4={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Od(){let t=(await dr()).voiceChat||{},n=t.provider||"openai",r=w4[n];if(!r)return null;let o=n==="openai"?await h4():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function c_(){let e=(await dr()).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 Jo(){let e=(await dr()).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}}fe();br();Rr();ra();import Ac from"node:fs/promises";import g_ from"node:path";import U4 from"node:os";import{randomUUID as y_}from"node:crypto";var lg={};mp(lg,{createComment:()=>A4,findIssueUrl:()=>k4,issueStates:()=>I4,mdToAdf:()=>d_,moveIssue:()=>$4,parseIssueRef:()=>b4,resolveIssue:()=>x4,uploadImage:()=>E4,upsertCard:()=>v4,whoami:()=>cg});var ga=e=>String(e.baseUrl||"").replace(/\/$/,""),u_=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Xo(e,t,n,r){let o=await fetch(`${ga(e)}${n}`,{method:t,headers:{authorization:u_(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 g4=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,y4=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function b4(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(g4);return r?r[1].toUpperCase():null}let n=t.match(y4);return n?n[1]:null}function k4(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 x4(e,t){let n;try{n=await Xo(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:`${ga(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function v4(e,t,{url:n,title:r,subtitle:o}){await Xo(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 S4=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function T4(e){let t=[],n=0;for(let r of e.matchAll(S4)){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 d_(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
714
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
717
|
+
`}async function Oc(e=Md){try{return JSON.parse(await no.readFile(e,"utf8"))}catch{return null}}var b2=new Set(["updating","restarting","rolling-back"]),Nd=e=>!!(e&&b2.has(e.status));async function Pd({to:e,from:t=Cd,kind:n=Qo(),reason:r="manual",statusFile:o=Md,spawnImpl:s=p2,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Rc.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!$c(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await Oc(o);if(Nd(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 no.mkdir(dt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?kf(await k2())||dt.resolve(dt.dirname(process.execPath),".."):null,d=y2({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=dt.join(dt.dirname(o),`update-${e}.sh`),f=dt.join(dt.dirname(o),"last.log");await no.writeFile(p,d,{mode:448}),await no.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=M_.existsSync("/run/systemd/system")&&await x2("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Bt(p)} >> ${Bt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(m){return await no.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${m?.message||m}`})),{ok:!1,reason:`could not spawn updater: ${m?.message||m}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:p,statusFile:o}}async function k2(){try{return await no.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function x2(e){for(let t of String(process.env.PATH||"").split(dt.delimiter))try{return await no.access(dt.join(t,e),M_.constants.X_OK),!0}catch{}return!1}var v2=45*6e4;async function D_({statusFile:e=Md,current:t=Cd,now:n=Date.now,staleMs:r=v2}={}){let o=await Oc(e);if(!o)return null;let s=a=>no.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Nd(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 j_({isBusy:e,start:t=Pd,onStuck:n=null,onStarted:r=null,tickMs:o=w2,stuckMs:s=h2,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){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 w=u;d();let m=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${m.ok?"started":`not started: ${m.reason}`}`);try{await r?.({...w,result:m})}catch{}};return{arm({to:f,from:w=Cd,kind:m=Qo()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:m,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}de();pt();var B_=(...e)=>fetch(...e);function L_(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function bg(e=process.env){if(!L_(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 B_(`${$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 kg(e=process.env){if(!L_(e))throw new Error("no plane configured \u2014 the roadmap lives 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 B_(`${$e(e)}/v1/roadmap`,{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{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function va(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await bg(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 Dd(e,t,{source:n="chat"}={}){return C(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}de();pt();var S2=(...e)=>fetch(...e);async function jd(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 S2(`${$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 T2(e,t=process.env){return jd("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function F_({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await T2(t,o):await va(e,o),a=await jd("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function xg(e,t=process.env){let n=await va(e,t),r=await jd("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function vg(e,t=process.env){return(await jd("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function U_(e,t,n,r=process.env){let{blueprint:o,items:s}=await xg(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 C(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}de();pt();var A2=(...e)=>fetch(...e);async function E2(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 A2(`${$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 H_=72,Sg=160;async function W_(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>H_)throw new Error(`the title is the choice as a LABEL \u2014 ${H_} 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 ${Sg} chars) a reader can act on without opening the record`);if(c.length>Sg)throw new Error(`the summary is one sentence \u2014 ${Sg} 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 C(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 q_(e,t=process.env){return E2("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}ow();de();import jc from"node:fs/promises";import Gd from"node:path";import gH from"node:os";vr();import _2 from"node:fs/promises";import K_ from"node:path";import I2 from"node:os";var G_=()=>K_.join(process.env.BOSTRAT_STATE_DIR||K_.join(process.env.HOME||I2.tmpdir(),".bostrat"),"integrations.json");async function hr(){try{return JSON.parse(await _2.readFile(G_(),"utf8"))||{}}catch{return{}}}var Bd=["linked","prOpened","prMerged","create"],Tg=["prompt","auto","off"],$2={create:["prompt","off"]},z_=e=>$2[e]??Tg;function Ld(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Bd){let s=z_(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 es(){let e=await hr(),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:Ld(e),defaultTeam:n}:null}async function Fd(){let e=await hr(),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:Ld(e)}}async function Ag(){return Ld(await hr())}async function V_(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Bd.join(", ")}`);for(let[r,o]of t){if(!Bd.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Bd.join(", ")}`);let s=z_(r);if(!s.includes(o))throw new Error(s.length<Tg.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Tg.join(", ")}`)}let n=await hr();return n.ticketFlow={...Ld(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Et(G_(),n,{mode:384}),n.ticketFlow}async function Cc(){let e=(await hr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function R2(){let e=(await hr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var O2={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Ud(){let t=(await hr()).voiceChat||{},n=t.provider||"openai",r=O2[n];if(!r)return null;let o=n==="openai"?await R2():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function J_(){let e=(await hr()).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 ts(){let e=(await hr()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}de();Tr();Pr();ca();import Nc from"node:fs/promises";import oI from"node:path";import nH from"node:os";import{randomUUID as sI}from"node:crypto";var _g={};Ap(_g,{createComment:()=>F2,findIssueUrl:()=>P2,issueStates:()=>W2,mdToAdf:()=>Z_,moveIssue:()=>q2,parseIssueRef:()=>N2,resolveIssue:()=>D2,uploadImage:()=>U2,upsertCard:()=>j2,whoami:()=>Eg});var Sa=e=>String(e.baseUrl||"").replace(/\/$/,""),X_=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function ns(e,t,n,r){let o=await fetch(`${Sa(e)}${n}`,{method:t,headers:{authorization:X_(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 C2=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,M2=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function N2(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(C2);return r?r[1].toUpperCase():null}let n=t.match(M2);return n?n[1]:null}function P2(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 D2(e,t){let n;try{n=await ns(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Sa(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function j2(e,t,{url:n,title:r,subtitle:o}){await ns(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 B2=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function L2(e){let t=[],n=0;for(let r of e.matchAll(B2)){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 Z_(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
718
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...L2(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 F2(e,t,n){let r=await ns(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:Z_(n)});return r?.id?`${Sa(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function Eg(e){let t=await ns(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Sa(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function U2(e,{issue:t,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${Sa(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:X_(e),"x-atlassian-token":"no-check",accept:"application/json"},body:s}),i=await a.json().catch(()=>null),c=Array.isArray(i)?i[0]:null;if(!a.ok||!c)throw new Error(`jira: attachment upload failed (HTTP ${a.status})`);return c.content??`${Sa(e)}/rest/api/3/attachment/content/${c.id}`}var H2={new:"unstarted",indeterminate:"started",done:"completed"},Y_=e=>H2[e?.key]??"started";async function W2(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([ns(e,"GET",`/rest/api/3/issue/${n}?fields=status`),ns(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Y_(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Y_(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function q2(e,t,n){await ns(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var Og={};Ap(Og,{createComment:()=>Z2,createIssue:()=>Rg,createProject:()=>$g,findIssueUrl:()=>J2,issueStates:()=>eH,moveIssue:()=>tH,parseIssueRef:()=>V2,resolveIssue:()=>Y2,teams:()=>Ta,uploadImage:()=>Q2,upsertCard:()=>X2,whoami:()=>Ig});var K2=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Ln(e,t,n={}){let r=await fetch(K2(),{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 G2=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,z2=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function V2(e){let t=String(e||"").trim(),n=t.match(G2);if(n)return n[1].toUpperCase();let r=t.match(z2);return r?r[1]:null}function J2(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 Y2(e,t){let r=(await Ln(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 X2(e,t,{url:n,title:r,subtitle:o}){let s=await Ln(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 Z2(e,t,n){let r=await Ln(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 Ig(e){let t=await Ln(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 Q2(e,{filename:t,contentType:n,buffer:r}){let o=await Ln(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
715
719
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
716
720
|
success
|
|
717
721
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
718
722
|
}
|
|
719
|
-
}`,{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 L4(e,t){let n=await Pn(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 F4(e,t,n){if(!(await Pn(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 ya(e){return((await Pn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var p_=255;async function dg(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>p_,a=s?`${o.slice(0,p_-1).trimEnd()}\u2026`:o,i=await Pn(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 pg(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Pn(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}}Li();var b_={linear:{api:fg,config:Yo},jira:{api:lg,config:Rd}};async function kg(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(b_)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Zo(e){let t=b_[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function xg(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 k_=()=>process.env.BOSTRAT_STATE_DIR||g_.join(process.env.HOME||U4.tmpdir(),".bostrat"),gg=()=>g_.join(k_(),"tickets.json"),x_=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Cd=e=>`${x_()}/track/${e}`,Xr=null,mg=null,hg=Promise.resolve();async function _c(){return Xr||(mg??=Ac.readFile(gg(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Xr=e,!t&&Le()){let{records:n,source:r}=await ot("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;Xr[s]=i}Object.keys(Xr).length&&await yg().catch(()=>{})}}return Xr}).finally(()=>mg=null),mg)}async function yg(){let e=JSON.stringify({tickets:Xr},null,2),t=`${gg()}.${process.pid}.tmp`;await Ac.mkdir(k_(),{recursive:!0}),await Ac.writeFile(t,e),await Ac.rename(t,gg())}async function Zr(e=null){if(hg=hg.then(yg,yg),await hg.catch(()=>{}),!e)return;let t=Xr?.[e];t?yt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Qt("tickets",e).catch(()=>{})}async function Dn(e){return(await _c())[e]??null}async function v_(){return{...await _c()}}async function S_(){return(await kg()).length>0}async function ba(e,t,{source:n="api"}={}){let r=await kg();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(p){a=p}}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 _c())[e.id]=c,await Zr(e.id),await C({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 Ec(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 Tc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await E_(e.id,c,"linked"),c}async function vg(e){let t=await _c();return t[e]?(delete t[e],await Zr(e),!0):!1}var bg=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function T_(e,t){if(!e.track?.cwd)return null;let n=$r(await G({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function H4(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${bg(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${bg(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function W4(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(bg(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Ec(e,t){let n=await Zo(t);if(!n)return;let r=await T_(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Cd(e),title:`Bostrat Track: ${t.track.name}`,subtitle:W4(t,r)})}function Md(e){let t=new URL(x_());return`${t.protocol}//${e}.${t.host}/`}var q4={linked:"linked",opened:"prOpened",merged:"prMerged"};function G4(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 A_(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Zr(e),await C({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 E_(e,t,n){let r=await Zo(t);if(!r)return;let o=r.cfg.ticketFlow?.[q4[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=xg(s,a),c=G4(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 A_(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await C({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:y_(),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 __(e){let t=await Dn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Zo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=xg(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Zr(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 Sg(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Dn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Zo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=xg(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 Zr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await A_(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function I_(e,t){let n=await Dn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Zo(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 C({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 $_(e,t,n){if(!t)throw new Error("offerId required");return Wr(`tkt:${e.id}:${t}`,()=>K4(e,t,n))}async function K4(e,t,n){let r=await G({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 Sg(e.id,o.payload.to?.id,{mode:"offer"})),await C({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 f_=50;async function R_(){let e=await Yo();if(!e)throw new Error("no ticketing integration configured");return ya(e)}function V4(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>f_)throw new Error(`too many issues (${n.length}); cap is ${f_} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function z4(e,t,n,r){let o=r??await ya(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 Dn(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 O_(e,t){let n=await Yo();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=V4(t),o=await ya(n),s=await z4(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=y_();return await C({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 C_(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await G({id:e.id,cwd:e.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await C({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 Yo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await ya(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let h=await dg(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await pg(a,{teamId:c.id,title:h.title,description:h.description,projectId:d,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await C({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 C({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 p=l.filter(h=>h.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await Dn(e.id)&&(f=await ba(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function Tc(e,t){let n=await Zo(e);n&&await n.api.createComment(n.cfg,e,t)}async function Y4(e,t,n){let r=await Zo(e);if(!r)return null;let o=await or(t);if(!o)return null;let s=cn(o,n);if(!s)return null;let a=await Ac.readFile(Vt(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 m_=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),wg=new Map;function J4(e,t){if(m_()<=0)return Ec(e,t);if(wg.has(e))return;let n=setTimeout(()=>{wg.delete(e),Dn(e).then(r=>r?Ec(e,r):null).catch(()=>{})},m_());n.unref?.(),wg.set(e,n)}var h_=new Map;async function w_(e,t,n){let r=h_.get(e)??0;Date.now()-r<600*1e3||(h_.set(e,Date.now()),await C({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function X4(e,{resolveTrack:t=null}={}){let n=await Dn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await kg())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 ba(s,o,{source:"chat"})}catch(a){await w_(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 Zr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Zr(e.trackId),await Ec(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 Zr(e.trackId),await Ec(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await T_(n,e.trackId).catch(()=>null):null;await Tc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
723
|
+
}`,{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 eH(e,t){let n=await Ln(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 tH(e,t,n){if(!(await Ln(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 Ta(e){return((await Ln(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Q_=255;async function $g(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>Q_,a=s?`${o.slice(0,Q_-1).trimEnd()}\u2026`:o,i=await Ln(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 Rg(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Ln(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}}Ki();var aI={linear:{api:Og,config:es},jira:{api:_g,config:Fd}};async function Bg(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(aI)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function rs(e){let t=aI[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Lg(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 iI=()=>process.env.BOSTRAT_STATE_DIR||oI.join(process.env.HOME||nH.tmpdir(),".bostrat"),Pg=()=>oI.join(iI(),"tickets.json"),cI=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Hd=e=>`${cI()}/track/${e}`,ro=null,Cg=null,Mg=Promise.resolve();async function Dc(){return ro||(Cg??=Nc.readFile(Pg(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(ro=e,!t&&Fe()){let{records:n,source:r}=await st("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;ro[s]=i}Object.keys(ro).length&&await Dg().catch(()=>{})}}return ro}).finally(()=>Cg=null),Cg)}async function Dg(){let e=JSON.stringify({tickets:ro},null,2),t=`${Pg()}.${process.pid}.tmp`;await Nc.mkdir(iI(),{recursive:!0}),await Nc.writeFile(t,e),await Nc.rename(t,Pg())}async function oo(e=null){if(Mg=Mg.then(Dg,Dg),await Mg.catch(()=>{}),!e)return;let t=ro?.[e];t?yt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Zt("tickets",e).catch(()=>{})}async function Fn(e){return(await Dc())[e]??null}async function lI(){return{...await Dc()}}async function uI(){return(await Bg()).length>0}async function Aa(e,t,{source:n="api"}={}){let r=await Bg();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(p){a=p}}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 Dc())[e.id]=c,await oo(e.id),await C({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Pc(e.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await Mc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await fI(e.id,c,"linked"),c}async function Fg(e){let t=await Dc();return t[e]?(delete t[e],await oo(e),!0):!1}var jg=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function dI(e,t){if(!e.track?.cwd)return null;let n=Nr(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function rH(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${jg(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${jg(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function oH(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(jg(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Pc(e,t){let n=await rs(t);if(!n)return;let r=await dI(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Hd(e),title:`Bostrat Track: ${t.track.name}`,subtitle:oH(t,r)})}function Wd(e){let t=new URL(cI());return`${t.protocol}//${e}.${t.host}/`}var sH={linked:"linked",opened:"prOpened",merged:"prMerged"};function aH(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 pI(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await oo(e),await C({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 fI(e,t,n){let r=await rs(t);if(!r)return;let o=r.cfg.ticketFlow?.[sH[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Lg(s,a),c=aH(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 pI(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await C({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:sI(),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 mI(e){let t=await Fn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await rs(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Lg(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await oo(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 Ug(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Fn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await rs(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Lg(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 oo(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await pI(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function hI(e,t){let n=await Fn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await rs(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 C({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 wI(e,t,n){if(!t)throw new Error("offerId required");return Jr(`tkt:${e.id}:${t}`,()=>iH(e,t,n))}async function iH(e,t,n){let r=await K({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 Ug(e.id,o.payload.to?.id,{mode:"offer"})),await C({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 eI=50;async function gI(){let e=await es();if(!e)throw new Error("no ticketing integration configured");return Ta(e)}function cH(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>eI)throw new Error(`too many issues (${n.length}); cap is ${eI} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function lH(e,t,n,r){let o=r??await Ta(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 Fn(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 yI(e,t){let n=await es();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=cH(t),o=await Ta(n),s=await lH(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=sI();return await C({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 bI(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await K({id:e.id,cwd:e.cwd}),s=o.find(w=>w.type==="ticket.create.offered"&&w.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(w=>w.type==="ticket.create.answered"&&w.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await C({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 es();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await Ta(a)).find(m=>m.id===r);if(!w)throw new Error("unknown team");c={id:w.id,key:w.key,name:w.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let w=await $g(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=w.id,l.push({kind:"epic",title:w.name,url:w.url})}for(let w of i.issues||[])try{let m=await Rg(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:m.key,title:m.title,url:m.url})}catch(m){u.push({title:w.title,message:m?.message||String(m)})}await C({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 C({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 p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await Fn(e.id)&&(f=await Aa(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function Mc(e,t){let n=await rs(e);n&&await n.api.createComment(n.cfg,e,t)}async function uH(e,t,n){let r=await rs(e);if(!r)return null;let o=await cr(t);if(!o)return null;let s=an(o,n);if(!s)return null;let a=await Nc.readFile(zt(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 tI=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Ng=new Map;function dH(e,t){if(tI()<=0)return Pc(e,t);if(Ng.has(e))return;let n=setTimeout(()=>{Ng.delete(e),Fn(e).then(r=>r?Pc(e,r):null).catch(()=>{})},tI());n.unref?.(),Ng.set(e,n)}var nI=new Map;async function rI(e,t,n){let r=nI.get(e)??0;Date.now()-r<600*1e3||(nI.set(e,Date.now()),await C({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function pH(e,{resolveTrack:t=null}={}){let n=await Fn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Bg())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 Aa(s,o,{source:"chat"})}catch(a){await rI(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 oo(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await oo(e.trackId),await Pc(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await oo(e.trackId),await Pc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await dI(n,e.trackId).catch(()=>null):null;await Mc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
720
724
|
|
|
721
|
-
Agent cost for this Track: ${
|
|
725
|
+
Agent cost for this Track: ${rH(o)}.`:"")),await fI(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await uH(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Mc(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
722
726
|
|
|
723
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
724
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
725
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
727
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Mc(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 Mc(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Wd(r.slug)}
|
|
728
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await dH(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await rI(e.trackId,s?.cwd??null,o)}}function kI({resolveTrack:e}){ot.on("event",t=>{pH(t,{resolveTrack:e}).catch(()=>{})})}async function xI(){if(!Fe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await st("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Dc(),r=0;for(let[o,s]of Object.entries(n))await yt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}Mo();ca();import fH from"ws";var vI=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function qd(e,t,n={}){let r=await fetch(`${vI()}/${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 Hg=(e,t)=>qd("chat.postMessage",e,t),os=(e,t)=>qd("chat.update",e,t);async function xt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function mH(e,t,n={}){let r=await fetch(`${vI()}/${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 SI(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await mH("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 qd("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var hH=2200;function TI({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 qd("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new fH(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 p=m=>{try{o.send(JSON.stringify(m?{envelope_id:d.envelope_id,payload:m}:{envelope_id:d.envelope_id}))}catch{}},f=!1,w=setTimeout(()=>{f=!0,p()},hH);Promise.resolve().then(()=>t(d)).then(m=>{clearTimeout(w),!f&&(f=!0,p(d.accepts_response_payload?m:void 0))},m=>{clearTimeout(w),f||(f=!0,p()),n(`envelope handler failed: ${m?.message||m}`)})}),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 _I=()=>process.env.BOSTRAT_STATE_DIR||Gd.join(process.env.HOME||gH.tmpdir(),".bostrat"),zg=()=>Gd.join(_I(),"slack.json"),Kd=null,Wg=null,qg=Promise.resolve();async function Jg(){return Kd||(Wg??=jc.readFile(zg(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>Kd=e).finally(()=>Wg=null),Wg)}async function AI(){let e=JSON.stringify({threads:Kd},null,2),t=`${zg()}.${process.pid}.tmp`;await jc.mkdir(_I(),{recursive:!0}),await jc.writeFile(t,e),await jc.rename(t,zg())}async function fn(){qg=qg.then(AI,AI),await qg.catch(()=>{})}async function Yg(e){return(await Jg())[e]??null}async function yH(e,t){let n=await Jg();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var ve=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Bc(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function bH(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Bc(n.replace(/\n+/g," "),t)}function II(e){let t=`*${ve(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${ve(e.repoLabel)} \xB7 \`${ve(e.branch||"?")}\` \xB7 lead *${ve(e.lead)}*`,r=e.kickoff?`> ${ve(Bc(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
729
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Hd(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Vg(e,t){await os(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:II(t)})}async function ao(e,t,n,r=void 0){return Hg(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var EI=new Set;function so(e){EI.add(e),e.catch(()=>{}).finally(()=>EI.delete(e))}var Le=e=>({response_type:"ephemeral",text:e}),kH="*/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._",xH=["claude","cursor","codex"];function Kg(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 Gg=(e,t)=>Le(`No live Track matches \u201C${ve(t)}\u201D. Live: ${e.map(n=>`\`${ve(n.name)}\``).join(" \xB7 ")||"none"}`);async function $I(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 xt(o,{...Le(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await xt(o,{...Le("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?Gd.basename(c.base):"fresh workspace"),lead:"claude"},u=await Hg(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:II(l),unfurl_links:!1});l.ts=u.ts,(await Jg())[i.id]=l,await fn(),await xt(o,{...Le(`\u2713 *${ve(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await xt(o,{...Le(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function vH(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Yg(n.id);s&&(s.archivedAt=Date.now(),await fn(),await Vg(t,s).catch(()=>{})),await xt(r,{...Le(`\u2713 *${ve(n.name)}* archived.`),replace_original:!0})}catch(s){await xt(r,{...Le(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function SH(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await xt(o,{...Le(`\u2713 Handing *${ve(n.name)}* to ${ve(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await xt(o,{...Le(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function TH(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 xt(r,{...Le(`\u2713 Stopped *${ve(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 xt(r,{...Le(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function AH(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Bc(`${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 *${ve(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function EH(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?Gd.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 *${ve(s.name)}* \u2014 ${ve(i)}${a.branch?` \`${ve(a.branch)}\``:""} \xB7 lead ${ve(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
726
730
|
`)+(o?`
|
|
727
|
-
_${o} archived_`:"")}async function
|
|
728
|
-
\u2713 *${
|
|
729
|
-
\u2713 _answered_`}}]})}async function
|
|
730
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
731
|
-
`)){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
|
|
732
|
-
`)}fe();km();import G2 from"node:fs/promises";import K2 from"node:path";jl();Hl();Rr();var Bd=e=>e.payload?.ts??e.ts,L2=12e4,Jt=e=>Math.round(e*1e4)/1e4,F2=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function U2(e,{since:t}){let{track:n,events:r,brief:o}=e,s=y=>Bd(y)>=t,a=Wn([hi(n?.name),hi(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let y of r)if(y.type==="subtask.delegated"&&y.boxId&&y.payload?.task){let I=Wn(y.payload.task);I&&i.set(y.boxId,I)}let c=new Map,l=y=>{let I=y||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=(y,{shape:I="initial",continuation:R=!1,text:P=null,ts:H=null}={})=>({textCat:y,shape:I,continuation:R,text:P,ts:H,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((y,I)=>Bd(y)-Bd(I)),p=0,f=0,h=0,w={turns:0};for(let y of d){let I=l(y.boxId),R=I.segments[I.segments.length-1],P=Bd(y);switch(I.lastTs!=null&&P>I.lastTs&&s(y)&&(R.activeMs+=Math.min(P-I.lastTs,L2)),I.lastTs=P,y.provider&&(I.provider=y.provider),y.type){case"turn.user":{let H=y.payload?.text;if(y.source==="hook"||!String(H||"").trim()||y.payload?.note||Rs(H))break;let j=Wn(H),A=!!y.payload?.kind,O=A||Hk(H),N=u(j,{shape:A?"injected":O?"follow-up":"novel-ask",continuation:!j&&O,text:Y_(H),ts:P});I.segments.push(N),s(y)&&(N.turns+=1);break}case"peer.message":{let H=y.payload?.text;if(!String(H||"").trim())break;let j=Wn(H),A=u(j,{shape:"peer",continuation:!j,text:Y_(H),ts:P});I.segments.push(A),s(y)&&(A.turns+=1);break}case"tool.called":{if(y.source==="transcript")break;let H=y.payload?.tool;H&&(R.tools[H]=(R.tools[H]||0)+1),s(y)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(y))break;let{usd:H,tokens:j,model:A,unpriced:O}=Wt(y);O&&(w.turns+=1),R.usd+=H,j&&(R.tokens.input+=j.input,R.tokens.output+=j.output,R.tokens.cacheRead+=j.cacheRead,R.tokens.cacheWrite+=j.cacheWrite),R.models[A]=(R.models[A]||0)+H,y.provider&&(R.provider=y.provider);break}case"handoff.done":s(y)&&(p+=1);break;case"subtask.merged":s(y)&&(f+=1);break;case"error":s(y)&&y.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let g=new Map,k={usd:0,turns:0,byShape:{},samples:[]},x=!1;for(let[y,I]of c){let R=null;for(let P of I.segments){let H=null,j=null;if(P.textCat)H=P.textCat.category,j="text";else if(i.get(y))H=i.get(y).category,j="boxTask";else if(P.continuation&&R)H=R,j="inherited";else{let $=Uk(P.tools);$?(H=$.category,j="toolMix"):a&&(H=a.category,j="hint")}if(H&&(R=H),!(P.usd>0||P.activeMs>0||P.turns>0||P.toolCalls>0))continue;let O=H??"uncategorized";g.has(O)||g.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let N=g.get(O);N.usd+=P.usd,N.activeMs+=P.activeMs,N.turns+=P.turns,N.toolCalls+=P.toolCalls,j&&(N.provenance[j]=(N.provenance[j]||0)+P.usd);for(let[$,T]of Object.entries(P.models))N.models[$]=(N.models[$]||0)+T;let z=P.provider||I.provider;z&&(N.providers[z]=(N.providers[z]||0)+P.usd),z==="cursor"&&P.usd===0&&(x=!0),H||(k.usd+=P.usd,k.turns+=P.turns,k.byShape[P.shape]=(k.byShape[P.shape]||0)+P.usd,P.usd>0&&P.text&&k.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:P.shape,usd:Jt(P.usd),ts:P.ts,text:P.text}))}}return{perCategory:g,uncategorized:k,handoffs:p,merged:f,failed:h,sawCursorActivity:x,unpricedTurns:w.turns}}function Ng(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0,l={usd:0,turns:0,byShape:{},samples:[]};for(let k of e||[]){let{track:x,events:y}=k;if(!y?.length){a+=1;continue}let{perCategory:I,uncategorized:R,handoffs:P,merged:H,failed:j,sawCursorActivity:A,unpricedTurns:O}=U2(k,{since:r});A&&(i=!0),c+=O,l.usd+=R.usd,l.turns+=R.turns;for(let[z,$]of Object.entries(R.byShape))l.byShape[z]=(l.byShape[z]||0)+$;l.samples.push(...R.samples);let N={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:P,subtasksMerged:H,subtasksFailed:j};for(let[z,$]of I){N.usd+=$.usd,N.activeMs+=$.activeMs,N.turns+=$.turns,N.categories[z]={usd:Jt($.usd),activeMs:$.activeMs};for(let[W,K]of Object.entries($.models))N.models[W]=Jt((N.models[W]||0)+K);for(let[W,K]of Object.entries($.providers))N.providers[W]=Jt((N.providers[W]||0)+K);o.has(z)||o.set(z,F2());let T=o.get(z);T.usd+=$.usd,T.activeMs+=$.activeMs,T.turns+=$.turns,T.toolCalls+=$.toolCalls;for(let[W,K]of Object.entries($.models))T.models[W]=(T.models[W]||0)+K;for(let[W,K]of Object.entries($.providers))T.providers[W]=(T.providers[W]||0)+K;for(let[W,K]of Object.entries($.provenance))T.provenance[W]=(T.provenance[W]||0)+K;let M=T.byTrack.get(x?.id)||{usd:0,activeMs:0};M.usd+=$.usd,M.activeMs+=$.activeMs,T.byTrack.set(x?.id,M)}(N.usd>0||N.activeMs>0||N.turns>0)&&(N.usd=Jt(N.usd),N.dominantCategory=q2(N.categories),s.push(N))}let u=[...o.values()].reduce((k,x)=>k+x.usd,0),d=[...o.values()].reduce((k,x)=>k+x.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,h=new Map((e||[]).map(k=>[k.track?.id,k.track?.name||k.track?.id])),w=[...o.entries()].map(([k,x])=>({category:k,sharePct:f>0?Math.round((p==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:p,usd:Jt(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:Oc(x.models),providers:Oc(x.providers),provenance:Oc(x.provenance),topWorkstreams:[...x.byTrack.entries()].sort((y,I)=>I[1].usd-y[1].usd||I[1].activeMs-y[1].activeMs).slice(0,2).map(([y,I])=>({trackId:y,name:h.get(y),usd:Jt(I.usd),activeMs:I.activeMs}))})).sort((k,x)=>x.usd-k.usd||x.activeMs-k.activeMs);s.sort((k,x)=>x.usd-k.usd||x.activeMs-k.activeMs);let g={};for(let k of o.values())for(let[x,y]of Object.entries(k.provenance))g[x]=(g[x]||0)+y;return{since:r,capturedAt:n,totalUsd:Jt(u),activeMs:d,turns:[...o.values()].reduce((k,x)=>k+x.turns,0),bullets:w,workstreams:s,provenance:Oc(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:Jt(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Oc(l.byShape),top:l.samples.sort((k,x)=>x.usd-k.usd).slice(0,H2)}}}}var H2=25,z_=160,Y_=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>z_?`${t.slice(0,z_).trimEnd()}\u2026`:t},W2=10;function J_(e,{thresholdPct:t=W2}={}){let n=e?.gaps?.uncategorized||{usd:0,turns:0},r=e?.totalUsd||0,o=n.usd||0,s=r>0?o/r*100:0,a=s<=t,i=a?[]:[`${s.toFixed(1)}% of the last 7d ($${o.toFixed(2)} of $${r.toFixed(2)}) is uncategorized (threshold ${t}%) \u2014 run \`npm run cost -- --uncategorized\` and add classifier table lines for the top asks`];return{ok:a,sharePct:Math.round(s*10)/10,thresholdPct:t,usd:Jt(o),totalUsd:Jt(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var q2=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},Oc=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Jt(n)]));var ts={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function V2(e){let t=String(e||"7d");return t in ts?{key:t,ms:ts[t]}:{key:"7d",ms:ts["7d"]}}function z2(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=cu(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 Y2(e){if(!e?.cwd)return null;try{let n=(await G2.readFile(K2.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 Pg(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}:V2(t),d=[];for(let h of e||[]){let w=await G(h),g=z2(h,w);i&&c<o&&(g=g.filter(k=>(k.ts??0)<=c)),d.push({track:h,events:g,brief:await Y2(h)})}let p=Ng(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:Ng(d,{windowMs:ts["7d"],now:o});return{window:l,...p,categorization:J_(f)}}var Cc=e=>`$${Number(e||0).toFixed(2)}`,X_=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},es=(e,t)=>String(e).padEnd(t);function Z_(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${Cc(e.totalUsd)} \xB7 ${X_(e.activeMs)} active \xB7 ${e.turns} turn(s) \xB7 as of ${new Date(e.capturedAt).toISOString()}`);let n=e.provenance||{};e.totalUsd>0&&Object.keys(n).length&&t.push("labeled by: "+Object.entries(n).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Math.round(i/e.totalUsd*100)}%`).join(" \xB7 ")),t.push(""),t.push("WHERE IT WENT");for(let a of e.bullets||[])t.push(` ${es(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${es(a.category,15)} ${es(Cc(a.usd),10)} ${es(X_(a.activeMs),6)} ${a.turns} turn(s)`+(a.topWorkstreams?.length?` \xB7 ${a.topWorkstreams.map(i=>i.name).join(", ")}`:""));let r=e.gaps||{};r.tracksWithoutEvents&&t.push(`${r.tracksWithoutEvents} track(s) have no attributed events yet`),r.cursorCostUnknown&&t.push("cursor time counted, cost unavailable"),r.unpricedTurns&&t.push(`WARNING: ${r.unpricedTurns} turn(s) unpriced \u2014 total under-reports`);let o=e.categorization;if(o){t.push(""),t.push((o.ok?"categorization ok: ":"CATEGORIZATION ALARM: ")+`${o.sharePct}% of the last 7d uncategorized (threshold ${o.thresholdPct}%) \xB7 ${o.inheritedPct}% inferred from the previous ask`);for(let a of o.problems||[])t.push(`WARNING: ${a}`)}let s=r.uncategorized;if(s){t.push(""),t.push(`UNCATEGORIZED RESIDUE \xB7 ${Cc(s.usd)} (${s.sharePct}%) \xB7 ${s.turns} turn(s) \xB7 by shape: `+(Object.entries(s.byShape||{}).filter(([,a])=>a>0).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Cc(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${es(Cc(a.usd),8)} ${es(a.shape,10)} ${es(String(a.track||"").slice(0,26),26)} | ${a.text}`);s.top?.length&&(t.push(""),t.push("novel-ask rows are vocabulary the classifier lacks: add a rule in agent/src/task-classifier.js (and a line in test/uncategorized-corpus.test.mjs). follow-up rows had no prior ask on their box."))}return t.join(`
|
|
733
|
-
`)}
|
|
734
|
-
`).slice(-3).join(" | ").slice(0,300);d!==
|
|
735
|
-
`).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
|
|
736
|
-
`).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
|
|
737
|
-
`),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
|
|
738
|
-
`,{mode:384}),
|
|
731
|
+
_${o} archived_`:"")}async function _H(e,t){let n=await ts();if(!n)return Le("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 Le("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?(so($I(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Le(`Starting *${ve(u)}* on ${ve(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:AH(u,i)}}case"archive":{if(!a.length)return Le("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Kg(await r.listTracks(),a.join(" "));return i?(so(vH(e,n,{track:i,responseUrl:t.response_url})),Le(`Archiving *${ve(i.name)}*\u2026`)):Gg(c,a.join(" "))}case"handoff":{let i=r.knownAgents??xH,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Le(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=Kg(await r.listTracks(),l);return u?(so(SH(e,n,{track:u,agent:c,responseUrl:t.response_url})),Le(`Handing *${ve(u.name)}* to ${ve(c)}\u2026`)):Gg(d,l)}case"stop":{if(!a.length)return Le("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Kg(await r.listTracks(),a.join(" "));return i?(so(TH(e,n,{track:i,responseUrl:t.response_url})),Le(`Stopping *${ve(i.name)}*\u2026`)):Gg(c,a.join(" "))}case"list":return Le(await EH(r));default:return Le(kH)}}async function IH(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Yg(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 fn(),await os(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ve(l||"question")}~
|
|
732
|
+
\u2713 *${ve(a)}*`}}]}).catch(()=>{})}}catch(c){await xt(n.response_url,Le(`Couldn't answer: ${c?.message||c}`))}}async function $H(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(p=>p.type==="pr.offered"&&p.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(p=>p.type==="pr.offer.answered"&&p.payload?.offerId===s))throw new Error("offer already answered");let d=Array.isArray(r.actions)?Fr.filter(p=>r.actions.includes(p)):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,zs(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 xt(n.response_url,Le(`Couldn't act on the offer: ${c?.message||c}`))}}async function RH(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 xt(n.response_url,Le(`Couldn't act on the offer: ${i?.message||i}`))}}async function OH(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await ts();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 xt(t.response_url,{...Le(`Starting *${ve(s)}*\u2026`),replace_original:!0}),so($I(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"?so(IH(e,r,t,o)):n.action_id==="bostrat-ship"?so($H(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&so(RH(e,r,t,o))}async function CH(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 yH(t.channel,t.thread_ts);if(!n)return;let r=await ts();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await ao(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function MH(e){return async t=>{if(t.type==="slash_commands")return _H(e,t.payload);if(t.type==="interactive"){await OH(e,t.payload);return}if(t.type==="events_api"){await CH(e,t.payload?.event);return}}}var NH=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 PH(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${ve(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:Bc(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 DH(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await os(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ve(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await ao(e,t,r.question||"The lead needs an answer",PH(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await fn()}async function jH(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 fn(),await os(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ve(s||"question")}~
|
|
733
|
+
\u2713 _answered_`}}]})}async function BH(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 ao(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${ve(Bc(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await fn()}async function LH(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await fn();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 os(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function FH(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 ao(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${ve(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 fn()}async function UH(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await fn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await os(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function HH(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??WH)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await SI(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${ve(o.name)}*${a}${o.group?` (group \`${ve(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${ve(a.name)}*`).join(", ");await ao(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${ve(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function WH(e,t){let n=await cr(e);if(!n)return null;let r=an(n,t);if(!r)return null;let o=await jc.readFile(zt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function qH(e,t){if(!NH.has(t.type))return;let n=await Yg(t.trackId);if(!n)return;let r=await ts();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 fn(),await Vg(r,n);return;case"turn.agent":{if(!o.text)return;let s=bH(o.text);if(!s)return;await ao(r,n,s,[{type:"section",text:{type:"mrkdwn",text:ve(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Hd(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return DH(r,n,t);case"prompt.answered":return jH(r,n,t);case"pr.offered":return BH(r,n,t);case"pr.offer.answered":return LH(r,n,t);case"pr.exists.offered":return FH(r,n,t);case"pr.exists.answered":return UH(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 ao(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 fn(),await Vg(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await ao(r,n,`Live preview ${o.name}: ${Wd(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${ve(o.name)}*: ${Wd(o.slug)}
|
|
734
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return HH(r,n,t,e.deps)}}function RI(e){let t={deps:e};ot.on("event",s=>{qH(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await ts()||(r&&clearInterval(r),r=null,n=TI({getToken:async()=>(await ts())?.appToken,onEnvelope:MH(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}nn();de();kn();ih();Vn();import KH from"node:fs/promises";function GH(e){let t=new Map;for(let n of String(e||"").split(`
|
|
735
|
+
`)){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 zH(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 VH=new Set(["Write","Edit","NotebookEdit"]),JH=220,YH=160;function XH(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(VH.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,JH)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=go(o.command.trim().replace(/\s+/g," ")).slice(0,YH));return r}function ZH(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?tn(t.cwd,t.sessionId):t?.cwd?sf(t.cwd):null}async function OI(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 C(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=qu(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:XH(e,o)});break;case"Stop":await QH(r,o,e,i);break;default:break}return a}async function QH(e,t,n,r){let o=[],s=ZH(n,t);if(s)try{o=GH(await KH.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=zH(await K(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let p=`${d.model||"unknown"}\0${d.speed||""}`,f=u.get(p)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let w of Object.keys(f.tokens))f.tokens[w]+=d.tokens[w];d.cacheWriteTtl&&(f.cacheWriteTtl??={m5:0,h1:0},f.cacheWriteTtl.m5+=d.cacheWriteTtl.m5,f.cacheWriteTtl.h1+=d.cacheWriteTtl.h1),f.messageIds.push(d.messageId),f.speed=d.speed||null,u.set(p,f)}for(let[d,p]of u){let f=d.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:Tn(f,p.tokens,void 0,p.speed?{speed:p.speed}:void 0),tokens:p.tokens,...p.speed?{speed:p.speed}:{},cacheWriteTtl:p.cacheWriteTtl,messageId:p.messageIds[p.messageIds.length-1],messageIds:p.messageIds,apiCalls:p.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}})}ds();Xp();Wu();de();de();Pr();de();Pr();Vn();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 CI=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),MI=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},zd=e=>Math.round(e*1e4)/1e4;async function Xg(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=CI(),d=0,p=[];for(let f of e||[]){let w=await K(f),m=Nr(w,{boxLabels:Lc(f)});if(m.turns===0){f.archivedAt?d++:p.push(f.name||f.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.asRecordedUsd||0,c+=m.unpricedTurns||0;for(let g of m.unpricedModels||[])l.add(g);MI(u,m.totalTokens);for(let[g,b]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:CI()},n[g].usd=zd(n[g].usd+b.usd),n[g].events+=b.events,MI(n[g].tokens,b.tokens);for(let[g,b]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=zd(r[g].usd+b.usd),r[g].events+=b.events;t.push({id:f.id,name:f.name,usd:m.totalUsd,rollup:m})}return t.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Ps(),fleet:{totalUsd:zd(o),asRecordedUsd:zd(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:d+p.length,tracksSilentArchived:d,tracksSilentLive:p},tracks:t}}var io=e=>`$${e.toFixed(4)}`,Ea=(e,t)=>String(e).padEnd(t);function NI(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 ${io(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 ${io(t.asRecordedUsd)} \xB7 current-rates total ${io(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${io(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(` ${Ea(a,26)} ${Ea(io(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(` ${Ea(a.name||a.id,30)} ${io(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Ea(c.label,40)} ${Ea(io(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,p)=>Number(d[0])-Number(p[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${Ea(io(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
736
|
+
`)}de();Rm();import aW from"node:fs/promises";import iW from"node:path";Gl();Xl();Pr();var Vd=e=>e.payload?.ts??e.ts,eW=12e4,Jt=e=>Math.round(e*1e4)/1e4,tW=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function nW(e,{since:t}){let{track:n,events:r,brief:o}=e,s=y=>Vd(y)>=t,a=zn([xi(n?.name),xi(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let y of r)if(y.type==="subtask.delegated"&&y.boxId&&y.payload?.task){let $=zn(y.payload.task);$&&i.set(y.boxId,$)}let c=new Map,l=y=>{let $=y||"unattributed";return c.has($)||c.set($,{segments:[u(null)],lastTs:null,provider:null}),c.get($)},u=(y,{shape:$="initial",continuation:O=!1,text:M=null,ts:H=null}={})=>({textCat:y,shape:$,continuation:O,text:M,ts:H,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((y,$)=>Vd(y)-Vd($)),p=0,f=0,w=0,m={turns:0};for(let y of d){let $=l(y.boxId),O=$.segments[$.segments.length-1],M=Vd(y);switch($.lastTs!=null&&M>$.lastTs&&s(y)&&(O.activeMs+=Math.min(M-$.lastTs,eW)),$.lastTs=M,y.provider&&($.provider=y.provider),y.type){case"turn.user":{let H=y.payload?.text;if(y.source==="hook"||!String(H||"").trim()||y.payload?.note||Ti(H))break;let P=zn(H),E=!!y.payload?.kind,R=E||i0(H),G=u(P,{shape:E?"injected":R?"follow-up":"novel-ask",continuation:!P&&R,text:DI(H),ts:M});$.segments.push(G),s(y)&&(G.turns+=1);break}case"peer.message":{let H=y.payload?.text;if(!String(H||"").trim())break;let P=zn(H),E=u(P,{shape:"peer",continuation:!P,text:DI(H),ts:M});$.segments.push(E),s(y)&&(E.turns+=1);break}case"tool.called":{if(y.source==="transcript")break;let H=y.payload?.tool;H&&(O.tools[H]=(O.tools[H]||0)+1),s(y)&&(O.toolCalls+=1);break}case"cost.recorded":{if(!s(y))break;let{usd:H,tokens:P,model:E,unpriced:R}=Wt(y);R&&(m.turns+=1),O.usd+=H,P&&(O.tokens.input+=P.input,O.tokens.output+=P.output,O.tokens.cacheRead+=P.cacheRead,O.tokens.cacheWrite+=P.cacheWrite),O.models[E]=(O.models[E]||0)+H,y.provider&&(O.provider=y.provider);break}case"handoff.done":s(y)&&(p+=1);break;case"subtask.merged":s(y)&&(f+=1);break;case"error":s(y)&&y.payload?.kind==="subtask-failed"&&(w+=1);break;default:break}}let g=new Map,b={usd:0,turns:0,byShape:{},samples:[]},x=!1;for(let[y,$]of c){let O=null;for(let M of $.segments){let H=null,P=null;if(M.textCat)H=M.textCat.category,P="text";else if(i.get(y))H=i.get(y).category,P="boxTask";else if(M.continuation&&O)H=O,P="inherited";else{let A=a0(M.tools);A?(H=A.category,P="toolMix"):a&&(H=a.category,P="hint")}if(H&&(O=H),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let R=H??"uncategorized";g.has(R)||g.set(R,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let G=g.get(R);G.usd+=M.usd,G.activeMs+=M.activeMs,G.turns+=M.turns,G.toolCalls+=M.toolCalls,P&&(G.provenance[P]=(G.provenance[P]||0)+M.usd);for(let[A,_]of Object.entries(M.models))G.models[A]=(G.models[A]||0)+_;let V=M.provider||$.provider;V&&(G.providers[V]=(G.providers[V]||0)+M.usd),V==="cursor"&&M.usd===0&&(x=!0),H||(b.usd+=M.usd,b.turns+=M.turns,b.byShape[M.shape]=(b.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&b.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:Jt(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:g,uncategorized:b,handoffs:p,merged:f,failed:w,sawCursorActivity:x,unpricedTurns:m.turns}}function Zg(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0,l={usd:0,turns:0,byShape:{},samples:[]};for(let b of e||[]){let{track:x,events:y}=b;if(!y?.length){a+=1;continue}let{perCategory:$,uncategorized:O,handoffs:M,merged:H,failed:P,sawCursorActivity:E,unpricedTurns:R}=nW(b,{since:r});E&&(i=!0),c+=R,l.usd+=O.usd,l.turns+=O.turns;for(let[V,A]of Object.entries(O.byShape))l.byShape[V]=(l.byShape[V]||0)+A;l.samples.push(...O.samples);let G={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:H,subtasksFailed:P};for(let[V,A]of $){G.usd+=A.usd,G.activeMs+=A.activeMs,G.turns+=A.turns,G.categories[V]={usd:Jt(A.usd),activeMs:A.activeMs};for(let[U,z]of Object.entries(A.models))G.models[U]=Jt((G.models[U]||0)+z);for(let[U,z]of Object.entries(A.providers))G.providers[U]=Jt((G.providers[U]||0)+z);o.has(V)||o.set(V,tW());let _=o.get(V);_.usd+=A.usd,_.activeMs+=A.activeMs,_.turns+=A.turns,_.toolCalls+=A.toolCalls;for(let[U,z]of Object.entries(A.models))_.models[U]=(_.models[U]||0)+z;for(let[U,z]of Object.entries(A.providers))_.providers[U]=(_.providers[U]||0)+z;for(let[U,z]of Object.entries(A.provenance))_.provenance[U]=(_.provenance[U]||0)+z;let N=_.byTrack.get(x?.id)||{usd:0,activeMs:0};N.usd+=A.usd,N.activeMs+=A.activeMs,_.byTrack.set(x?.id,N)}(G.usd>0||G.activeMs>0||G.turns>0)&&(G.usd=Jt(G.usd),G.dominantCategory=sW(G.categories),s.push(G))}let u=[...o.values()].reduce((b,x)=>b+x.usd,0),d=[...o.values()].reduce((b,x)=>b+x.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(b=>[b.track?.id,b.track?.name||b.track?.id])),m=[...o.entries()].map(([b,x])=>({category:b,sharePct:f>0?Math.round((p==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:p,usd:Jt(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:Fc(x.models),providers:Fc(x.providers),provenance:Fc(x.provenance),topWorkstreams:[...x.byTrack.entries()].sort((y,$)=>$[1].usd-y[1].usd||$[1].activeMs-y[1].activeMs).slice(0,2).map(([y,$])=>({trackId:y,name:w.get(y),usd:Jt($.usd),activeMs:$.activeMs}))})).sort((b,x)=>x.usd-b.usd||x.activeMs-b.activeMs);s.sort((b,x)=>x.usd-b.usd||x.activeMs-b.activeMs);let g={};for(let b of o.values())for(let[x,y]of Object.entries(b.provenance))g[x]=(g[x]||0)+y;return{since:r,capturedAt:n,totalUsd:Jt(u),activeMs:d,turns:[...o.values()].reduce((b,x)=>b+x.turns,0),bullets:m,workstreams:s,provenance:Fc(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:Jt(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Fc(l.byShape),top:l.samples.sort((b,x)=>x.usd-b.usd).slice(0,rW)}}}}var rW=25,PI=160,DI=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>PI?`${t.slice(0,PI).trimEnd()}\u2026`:t},oW=10;function jI(e,{thresholdPct:t=oW}={}){let n=e?.gaps?.uncategorized||{usd:0,turns:0},r=e?.totalUsd||0,o=n.usd||0,s=r>0?o/r*100:0,a=s<=t,i=a?[]:[`${s.toFixed(1)}% of the last 7d ($${o.toFixed(2)} of $${r.toFixed(2)}) is uncategorized (threshold ${t}%) \u2014 run \`npm run cost -- --uncategorized\` and add classifier table lines for the top asks`];return{ok:a,sharePct:Math.round(s*10)/10,thresholdPct:t,usd:Jt(o),totalUsd:Jt(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var sW=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},Fc=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Jt(n)]));var as={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function cW(e){let t=String(e||"7d");return t in as?{key:t,ms:as[t]}:{key:"7d",ms:as["7d"]}}function lW(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=yu(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 uW(e){if(!e?.cwd)return null;try{let n=(await aW.readFile(iW.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 Qg(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}:cW(t),d=[];for(let w of e||[]){let m=await K(w),g=lW(w,m);i&&c<o&&(g=g.filter(b=>(b.ts??0)<=c)),d.push({track:w,events:g,brief:await uW(w)})}let p=Zg(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:Zg(d,{windowMs:as["7d"],now:o});return{window:l,...p,categorization:jI(f)}}var Uc=e=>`$${Number(e||0).toFixed(2)}`,BI=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},ss=(e,t)=>String(e).padEnd(t);function LI(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${Uc(e.totalUsd)} \xB7 ${BI(e.activeMs)} active \xB7 ${e.turns} turn(s) \xB7 as of ${new Date(e.capturedAt).toISOString()}`);let n=e.provenance||{};e.totalUsd>0&&Object.keys(n).length&&t.push("labeled by: "+Object.entries(n).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Math.round(i/e.totalUsd*100)}%`).join(" \xB7 ")),t.push(""),t.push("WHERE IT WENT");for(let a of e.bullets||[])t.push(` ${ss(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${ss(a.category,15)} ${ss(Uc(a.usd),10)} ${ss(BI(a.activeMs),6)} ${a.turns} turn(s)`+(a.topWorkstreams?.length?` \xB7 ${a.topWorkstreams.map(i=>i.name).join(", ")}`:""));let r=e.gaps||{};r.tracksWithoutEvents&&t.push(`${r.tracksWithoutEvents} track(s) have no attributed events yet`),r.cursorCostUnknown&&t.push("cursor time counted, cost unavailable"),r.unpricedTurns&&t.push(`WARNING: ${r.unpricedTurns} turn(s) unpriced \u2014 total under-reports`);let o=e.categorization;if(o){t.push(""),t.push((o.ok?"categorization ok: ":"CATEGORIZATION ALARM: ")+`${o.sharePct}% of the last 7d uncategorized (threshold ${o.thresholdPct}%) \xB7 ${o.inheritedPct}% inferred from the previous ask`);for(let a of o.problems||[])t.push(`WARNING: ${a}`)}let s=r.uncategorized;if(s){t.push(""),t.push(`UNCATEGORIZED RESIDUE \xB7 ${Uc(s.usd)} (${s.sharePct}%) \xB7 ${s.turns} turn(s) \xB7 by shape: `+(Object.entries(s.byShape||{}).filter(([,a])=>a>0).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Uc(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${ss(Uc(a.usd),8)} ${ss(a.shape,10)} ${ss(String(a.track||"").slice(0,26),26)} | ${a.text}`);s.top?.length&&(t.push(""),t.push("novel-ask rows are vocabulary the classifier lacks: add a rule in agent/src/task-classifier.js (and a line in test/uncategorized-corpus.test.mjs). follow-up rows had no prior ask on their box."))}return t.join(`
|
|
737
|
+
`)}Vn();var co=36e5,WI=24*co,dW=400,pW=12,is=e=>Math.round(e*1e4)/1e4;function ey({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 as?String(e||"7d"):"7d",i=as[a];return{key:a,since:i==null?null:r-i,until:r}}var fW=(e,t)=>e!=null&&t-e<=48*co?co:WI;function FI(e,t){if(t===co)return Math.floor(e/co)*co;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function mW(e,t){if(t===co)return e+co;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var UI=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),HI=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},hW=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,is(n)]));async function qI(e,t={}){let n=t.now??Date.now(),r=ey({...t,now:n}),o=r.since==null?WI:fW(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=UI(),d=0,p=0,f=0,w=0,m=0,g=0,b=new Set,x=new Map,y=[],$=[],O=[],M=null;for(let E of e||[]){let G=(await K(E)).filter(_=>_.type==="cost.recorded");if(!G.length){E.archivedAt||O.push(E.name||E.id);continue}let V=[];for(let _ of G){let N=_.ts??0;s(N)?V.push(_):i(N)&&(g+=Wt(_).usd)}if(!V.length)continue;let A=Nr(V,{boxLabels:Lc(E)});d+=A.totalUsd,p+=A.turns,f+=A.repricedTurns||0,w+=A.asRecordedUsd||0,m+=A.unpricedTurns||0;for(let _ of A.unpricedModels||[])b.add(_);HI(u,A.totalTokens);for(let[_,N]of Object.entries(A.byModel))c[_]||={usd:0,events:0,tokens:UI()},c[_].usd=is(c[_].usd+N.usd),c[_].events+=N.events,HI(c[_].tokens,N.tokens);for(let[_,N]of Object.entries(A.byProvider))l[_]||={usd:0,events:0},l[_].usd=is(l[_].usd+N.usd),l[_].events+=N.events;for(let _ of V){let{usd:N}=Wt(_);if(!N)continue;let U=FI(_.ts??0,o),z=x.get(U)??{t:U,total:0,byProvider:{}};z.total+=N;let ue=_.provider||"unknown";z.byProvider[ue]=(z.byProvider[ue]||0)+N,x.set(U,z),(M==null||U<M)&&(M=U)}y.push({id:E.id,name:E.name||E.id,archived:!!E.archivedAt,usd:A.totalUsd,turns:A.turns,providers:Object.keys(A.byProvider)});for(let[_,N]of Object.entries(A.byBox))$.push({trackId:E.id,trackName:E.name||E.id,boxId:_,...N})}let H=r.since!=null?FI(r.since,o):M,P=[];for(let E=H,R=0;E!=null&&E<r.until&&R<dW;E=mW(E,o),R++){let G=x.get(E);P.push(G?{t:E,total:is(G.total),byProvider:hW(G.byProvider)}:{t:E,total:0,byProvider:{}})}return y.sort((E,R)=>R.usd-E.usd),$.sort((E,R)=>R.usd-E.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Ps(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:P,fleet:{totalUsd:is(d),asRecordedUsd:is(w),turns:p,repricedTurns:f,unpricedTurns:m,unpricedModels:[...b],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:y.length,tracksSilentLive:O},prior:a!=null?{totalUsd:is(g),since:r.since-a,until:r.since}:null,tracks:y.slice(0,50),boxes:$.slice(0,pW)}}Uu();var wW=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),gW=20,yW=40;async function KI(e,t={}){let n=t.now??Date.now(),r=ey({...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 K(l),d=0,p=Lc(l);for(let f of u){if(!wW.has(f.type))continue;let w=f.type==="code.rework"?f.payload?.ts??f.ts:f.ts;f.type!=="rework.cause.answered"&&!o(w??0)||(f.type==="code.committed"&&d++,s.push(f.trackId?f:{...f,trackId:l.id}))}d&&i++;for(let f of l.boxes||[])a[`${l.id}/${f.name}`]={trackName:l.name||l.id,label:p[f.name]||f.name}}let c=VS(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:zr,decayEndDays:Xm,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,intents:c.intents,noCodeDelta:c.noCodeDelta,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,gW),fixes:c.fixes.slice(0,yW)}}import bW from"node:fs";import GI from"node:fs/promises";import Hc from"node:os";import kW from"node:path";Wh();ct();var xW=1024*1024,Jd=e=>Math.round(e/xW);async function vW(e){try{let t=await GI.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Jd(r),diskTotalMb:Jd(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function SW(){try{return(await Qt()).filter(t=>t.isBostrat).length}catch{return null}}async function TW(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await GI.readdir(kW.join(_c,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function zI({stateDir:e=_c,now:t=Date.now}={}){let n=Hc.totalmem(),[r,o,s,a]=await Promise.all([od().catch(()=>null),vW(bW.existsSync(e)?e:Hc.tmpdir()),SW(),TW()]),i=Hc.cpus()?.length||null,c=Hc.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?Jd(n-r):null,memTotalMb:Jd(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Hc.uptime())}}Eh();de();Of();Va();Vn();de();Vn();var AW=864e5;function EW(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=Tn(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 VI(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*AW,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await K(u);for(let p of d){if(!p.ts||p.ts<o)continue;let f=EW(p,{tolerance:n});if(!f)continue;a++;let w=f.speed?`${f.model} (${f.speed})`:f.model,m=s[w]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};m.rows++,m.asRecordedUsd+=f.asRecorded,m.expectedUsd+=f.expected,f.divergent&&(i++,m.divergent++,c.length<10&&c.push({track:u.name||u.id,...f}))}}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}}Sh();pw();hu();pt();An();import _a from"node:fs/promises";import _W from"node:fs";import mt from"node:path";import IW from"node:os";import{execFile as $W}from"node:child_process";import{promisify as RW}from"node:util";var OW=RW($W),JI=process.env.BOSTRAT_STATE_DIR||mt.join(process.env.HOME||IW.tmpdir(),".bostrat"),Yd=!1,ty=null;async function ny(e,t){let n=await _a.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await _a.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await _a.mkdir(mt.dirname(t),{recursive:!0}),await _a.copyFile(e,t),!0)}var CW=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 Xd(e,t,{skip:n=null}={}){let r=0,o=await _a.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(CW.has(s.name)||n?.has(s.name))continue;let a=mt.join(e,s.name);s.isDirectory()?r+=await Xd(a,mt.join(t,s.name)):s.isFile()&&(r+=await ny(a,mt.join(t,s.name))?1:0)}return r}async function YI({stateDir:e=JI,dest:t=process.env.BOSTRAT_SYNC_DIR||`${JI}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await _a.mkdir(t,{recursive:!0});let o=0;o+=await ny(mt.join(e,"tracks.json"),mt.join(t,"tracks.json"))?1:0,o+=await ny(mt.join(e,"events.jsonl"),mt.join(t,"events.jsonl"))?1:0,o+=await Xd(mt.join(e,"events"),mt.join(t,"events")),o+=await Xd(mt.join(e,"memory"),mt.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Xd(mt.join(u.cwd,".bostrat"),mt.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>OW("git",["-C",t,...u]);_W.existsSync(mt.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 Mp(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||Yd))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Yd&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Yd=!1,ty=null}catch(u){Yd=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
738
|
+
`).slice(-3).join(" | ").slice(0,300);d!==ty&&(ty=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)}}}cw();nn();vo();ct();Ua();import{execFile as MW}from"node:child_process";import{promisify as NW}from"node:util";import{randomUUID as PW}from"node:crypto";import DW from"node:fs";import jW from"node:os";import BW from"node:path";var uo=NW(MW),Zd=e=>new Promise(t=>setTimeout(t,e)),po=ef,ry=process.env.BOSTRAT_USAGE_PROBE_CWD||BW.join(jW.homedir(),".bostrat","usage-probe"),LW=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,FW=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,UW=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,XI=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,lo=null,Wc=null,qc=null;async function HW(){try{return await uo(X,Z("has-session","-t",po)),!0}catch{return!1}}function WW(){lo&&clearTimeout(lo),lo=setTimeout(()=>qW().catch(()=>{}),LW),lo.unref&&lo.unref()}async function qW(){lo&&(clearTimeout(lo),lo=null);try{await uo(X,Z("kill-session","-t",po))}catch{}}async function KW(){let e=Date.now()+UW;for(;;){let t=await oy().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Zd(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Zd(500)}}async function GW(){if(await HW())return;if(!await Er())throw new Error("host tmux server not running \u2014 start globo-tmux.service");DW.mkdirSync(ry,{recursive:!0}),await yr(ry);let e=PW();await uo(X,Z("new-session","-d","-s",po,"-c",ry,"-x","200","-y","60",...ri()));let t=`${Qp} --session-id ${e}`;await uo(X,Z("send-keys","-t",po,t,"Enter")),await KW()}async function oy(){let{stdout:e}=await uo(X,Z("capture-pane","-t",po,"-p"));return e}async function zW(){await GW(),await uo(X,Z("send-keys","-t",po,"-l","/usage")),await uo(X,Z("send-keys","-t",po,"Enter"));let e=Date.now()+FW,t="";for(;await Zd(400),t=await oy(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Zd(900),t=await oy(),await uo(X,Z("send-keys","-t",po,"Escape")).catch(()=>{}),WW(),t}function VW(e){let t=String(e||"").split(`
|
|
739
|
+
`).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 JW(e){let t=String(e||"").split(`
|
|
740
|
+
`).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 ZI({fresh:e=!1}={}){return!e&&Wc&&Date.now()-Wc.at<XI?Wc.data:qc||(qc=(async()=>{try{let t=await zW(),n=VW(t),r={...n,contributors:JW(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Wc={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 Wc={at:Date.now()-(XI-3e3),data:n},n}finally{qc=null}})(),qc)}var YW=262144;function XW(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 ZW=new Set([300]),QW=new Set([10080]);function eq(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:ZW.has(t)?"session":QW.has(t)?"week":"other"}function tq(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 nq(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?XW((o-n)/1e3):null,resetsAt:o}}var Kc=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function rq(e,t=Date.now()){let n=String(e||"").split(`
|
|
741
|
+
`),r=!1;for(let o=n.length-1;o>=0;o--){let s=n[o].trim();if(!s||s[0]!=="{"||!s.includes("token_count"))continue;let a;try{a=JSON.parse(s)}catch{continue}let i=a?.payload;if(i?.type!=="token_count")continue;r=!0;let c=i.rate_limits;if(!c)return Kc(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Kc(r);[c.primary,c.secondary].forEach((f,w)=>{let m=nq(f,l,t);if(!m)return;let g=eq(f.window_minutes);g===null&&(g=w===0?"session":"week"),g==="session"&&!u.session?u.session=m:g==="week"&&!u.week?u.week=m:(u.other??=[]).push({label:tq(f.window_minutes),...m})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let p=c.credits;return p&&(p.has_credits||p.unlimited)&&(u.credits={balance:String(p.balance??""),unlimited:!!p.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...Kc(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Kc(r)}return Kc(r)}async function sy(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await ko(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 _t(n.file,YW),o=rq(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 QI(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 Qd({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 sy(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await sy(null))}return n.has("cursor")&&r.push(await Qd({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Qd(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?sy(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await ZI({fresh:t})}}kr();pt();pt();function oq(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}var sq=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),ep=(e,t)=>Object.assign(new Error(e),{status:t});async function e$(e,t){if(!oq(t))throw ep("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${$e(t)}${e}`,{headers:sq(t)})}catch(o){throw ep(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw ep(r?.error||`plane replied ${n.status}`,n.status);return r}async function t$(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await e$(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function n$(e,{env:t=process.env}={}){if(!e)throw ep("message id required",400);return(await e$(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}Ni();$u();function ay(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 aq(e){let[t,n]=e.split("/"),r=ay(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 r$(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(aq).filter(Boolean)}function iq(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function o$(e){let t=r$(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:iq,allowed(r){if(n)return!0;let o=ay((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function s$(e,t){let n=r$(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=ay(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import a$ from"node:crypto";import tp from"node:fs";import cq from"node:path";function i$(e=process.env,{stateDir:t=_c,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=cq.join(t,"api-token");try{let a=tp.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=a$.randomBytes(32).toString("base64url");try{tp.mkdirSync(t,{recursive:!0}),tp.writeFileSync(o,s+`
|
|
742
|
+
`,{mode:384}),tp.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Gc(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 c$(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:a$.timingSafeEqual(n,r)}function l$(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 lq=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/,/^\/api\/tracks\/adopt\b/,/^\/api\/tracks\/[^/]+\/transfer-out\b/,/^\/api\/node\/self-update\b/,/^\/api\/changelog\/(publish|retract)\b/,/^\/api\/tracks\/[^/]+\/loop-(update|delete)-offer\b/],uq=e=>lq.some(t=>t.test(e));function u$({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=l$(o);return c$(c,e)?a():uq(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||Gc(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 d$(e,t,{enforce:n=!0}={}){return!n||c$(l$(e),t)?!0:Gc(e)}kn();ed();ed();import dq from"node:http";import pq from"node:net";function np(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 rp(e){let t=np(e.headers.host);return t?{slug:t,view:Lh(t)}:null}var fq=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),mq=e=>{let t=fq(e.name);return`<!doctype html><meta charset="utf-8">
|
|
739
743
|
<meta http-equiv="refresh" content="3">
|
|
740
744
|
<title>${t} \u2014 starting</title>
|
|
741
745
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
742
746
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
743
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
747
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function p$(e){if(!/[?&]token=/.test(e))return e;try{let t=new URL(e,"http://view");return t.searchParams.delete("token"),t.pathname+t.search}catch{return e}}function f$(e,t,n){let r=rp(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(mq(o));if(o.state!=="live"&&o.state!=="unhealthy")return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...e.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":e.headers.host,"x-forwarded-proto":e.headers["x-forwarded-proto"]||"http"};delete a.authorization,delete a.cookie,delete a["x-bostrat-token"];let i=dq.request({host:"127.0.0.1",port:o.port,method:e.method,path:p$(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 m$(e,t,n){let r=rp(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
|
|
744
748
|
\r
|
|
745
749
|
bostrat: no live view "${s}"\r
|
|
746
|
-
`),t.destroy(),!0;let a=
|
|
750
|
+
`),t.destroy(),!0;let a=pq.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${p$(e.url)} HTTP/1.1`];for(let[l,u]of Object.entries(e.headers))l!=="host"&&(l==="authorization"||l==="cookie"||l==="x-bostrat-token"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
747
751
|
`)+`\r
|
|
748
752
|
\r
|
|
749
|
-
`),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 II(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}.${eW(n.host)}/`}function eW(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 Kd(t)&&Ah(n[0])?n.slice(1).join("."):t}ra();jh();ed();eh();fe();import $I from"node:os";import{randomUUID as qg}from"node:crypto";var tW=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||$I.hostname(),nodeName:e.BOSTRAT_NODE_NAME||$I.hostname()});var RI=32768,OI=120,CI=1e3,MI=new Set(["claude","codex","cursor"]),nW=new Set(["off","transition","nag"]),rW=[[0,59],[0,23],[1,31],[1,12],[0,6]];function oW(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=rW[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var fn=e=>String(e??"").trim(),rs={name(e){let t=fn(e);if(!t)throw new Error("a loop needs a name");if(t.length>OI)throw new Error(`name is too long (${t.length}); cap is ${OI}`);return t},schedule(e){let t=fn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!oW(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=fn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>RI)throw new Error(`prompt is too long (${t.length} chars); cap is ${RI}`);return t},expectation(e){let t=fn(e)||null;if(t&&t.length>CI)throw new Error(`expectation is too long (${t.length}); cap is ${CI}`);return t},notify(e){let t=fn(e)||"off";if(!nW.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=fn(typeof t=="string"?t:t?.provider)||"claude";if(!MI.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...MI].join(", ")}`);let r=typeof t=="object"&&t&&fn(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function sW(e,t){let n=e&&typeof e=="object"?e:{},r=rs.name(n.name),o=rs.schedule(n.schedule),s=rs.prompt(n.prompt),a=rs.expectation(n.expectation),i=rs.notify(n.notify),c=rs.assignment(n.assignment),l=fn(t?.workspace?.repo)||fn(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:d}=tW();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function PI(e,t){let n=sW(t,e),r=qg();return await C({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 DI(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await G({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 C({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 C({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 C({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 Wg=["name","schedule","prompt","expectation","notify","assignment","enabled"],aW=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function jI(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 BI(e,t){let n=fn(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function iW(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=>!Wg.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: ${Wg.join(", ")}`)}let o={};for(let a of Wg){if(!(a in n))continue;let i=rs[a](n[a]);(a==="assignment"?aW(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 LI(e,t,n){let r=jI(t),{patch:o,changed:s}=iW(n,r),a=qg(),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 C({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var NI=600;async function FI(e,t,n){let r=fn(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>NI)throw new Error(`reason is too long (${r.length}); cap is ${NI}`);let o=jI(t),s=qg(),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 C({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var UI=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await G({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 HI(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await UI(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await C(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 C(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 WI(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await UI(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await C(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 C(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}}fe();import{randomUUID as cW}from"node:crypto";var Vg=20,lW=300,Gg=120,qI=120,GI=2e3,zd=new Set(["build","ops","debt","spike"]),KI=new Set(["active","done","abandoned"]),zg=new Set(["circuit.create","circuit.update","circuit.delete"]),uW=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Bc=new Set([...zg,...uW]),dW=["name","description","status","type"],pW=["person","startDate","endDate","estUsd"],fW=["person","label","startDate","endDate"],mW=/^\d{4}-\d{2}-\d{2}$/,Lt=e=>String(e??"").trim(),Yg=e=>typeof e=="string"&&e.startsWith("@"),YI=864e5;function JI(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*YI).toISOString().slice(0,10)}function hW(e){return new Date(Date.parse(`${JI(e)}T00:00:00Z`)+6*YI).toISOString().slice(0,10)}var VI=(e,t)=>{let n=Lt(e);if(!mW.test(n))throw new Error(`${t} must be YYYY-MM-DD, got "${e??""}"`);if(!Number.isFinite(Date.parse(`${n}T00:00:00Z`)))throw new Error(`${t} "${n}" is not a real date`);return n};function wW(e,t){let n=VI(e,"startDate"),r=VI(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=JI(n),s=hW(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var zI=e=>{if(e==null||e==="")return null;let t=Number(e);if(!Number.isFinite(t)||t<0)throw new Error(`estUsd must be a non-negative number, got "${e}"`);return t},ao=(e,t,n)=>{let r=Lt(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function Kg(e,t){let n=Lt(t);if(!n)throw new Error("a circuit name or id is required");if(Yg(n))return{ref:n,id:null,name:n.slice(1)};let r=e.find(i=>i.id===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(i=>String(i.name).toLowerCase()===o);if(s.length===1)return s[0];if(s.length>1)throw new Error(`"${n}" names ${s.length} circuits \u2014 use an id (bostrat-circuit ls)`);let a=e.filter(i=>String(i.name).toLowerCase().includes(o)).slice(0,5);throw new Error(a.length?`no circuit named "${n}" \u2014 near matches: ${a.map(i=>`${i.name} (${i.id})`).join(", ")}`:`no circuit named "${n}" \u2014 propose a circuit.create op if it should exist (bostrat-circuit ls lists yours)`)}function gW(e,t){let n=Lt(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||Yg(n))return{ref:n,id:null,label:n==="me"?"you":n.slice(1)};let r=e.find(a=>a.personId===n);if(r)return{...r,id:r.personId};let o=n.toLowerCase(),s=e.filter(a=>String(a.label).toLowerCase()===o);if(s.length===1)return{...s[0],id:s[0].personId};throw s.length>1?new Error(`"${n}" names ${s.length} roadmap rows \u2014 use a person id`):new Error(e.length?`no roadmap row for "${n}" \u2014 rows: ${e.map(a=>`${a.label} (${a.personId})`).join(", ")}, or propose a person.create op`:`no roadmap rows yet \u2014 use "me", or propose a person.create op for "${n}"`)}function yW(e,t,{allow:n=Bc}={}){let r=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!r)throw new Error("a plan must be a JSON object with an `ops` array");let o=Array.isArray(r.ops)?r.ops:null;if(!o?.length)throw new Error("a plan needs at least one op in `ops`");if(o.length>Vg)throw new Error(`too many ops (${o.length}); cap is ${Vg} \u2014 propose one intent at a time`);let s=Array.isArray(t?.circuits)?t.circuits:[],a=Array.isArray(t?.people)?t.people:[],i=Array.isArray(t?.slots)?t.slots:[],c=Array.isArray(t?.reservations)?t.reservations:[],l={circuits:{},slots:{},reservations:{}},u={},d=new Set,p=!1,f=!1,h=y=>{y.id&&(l.circuits[y.id]={name:y.name,status:y.status,type:y.type??"build",updatedAt:y.updatedAt??null},u[y.id]=y.name)},w=y=>{y.id&&(u[y.id]=y.label)},g=(y,I)=>{let R=Lt(y);if(!R)return null;if(d.has(R))throw new Error(`ref "${R}" is used by two ops \u2014 each ref names one thing`);return d.add(R),u[`@${R}`]=I,R},k=o.map((y,I)=>{let R=y&&typeof y=="object"?y:{},P=Lt(R.op),H=`op ${I+1}`;if(!Bc.has(P))throw new Error(`${H}: unknown op "${P}" \u2014 one of ${[...Bc].join(", ")}`);if(!n.has(P))throw new Error(`${H}: "${P}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=$=>{throw new Error(`${H} (${P}): ${$}`)},A=$=>{let T=R.set&&typeof R.set=="object"&&!Array.isArray(R.set)?R.set:null;T||j("needs a `set` object naming the fields to change");let M=Object.keys(T).filter(W=>!$.includes(W));return M.length&&j(`unknown field(s) ${M.join(", ")} \u2014 settable: ${$.join(", ")}`),Object.keys(T).length||j("`set` names no fields"),T},O=$=>{let{startDate:T,endDate:M,snapped:W}=wW($.startDate,$.endDate);return W&&(p=!0),{startDate:T,endDate:M,snapped:W}},N=$=>{let T=gW(a,$);return Lt($)==="me"&&(f=!0),w(T),{key:T.id??Lt($),label:T.label}},z=($,T,M,W,K)=>{let be=Lt($);be||j(`needs a ${W} id`);let se=T.find(we=>we[M]===be);return se||j(`no ${W} "${be}" \u2014 see \`bostrat-roadmap ls\``),K[be]={...se},se};switch(P){case"circuit.create":{let $=ao(R.name,qI,"name");$||j("a circuit needs a name");let T=Lt(R.type)||"build";zd.has(T)||j(`bad type "${T}" (${[...zd].join(" | ")})`);let M=s.find(K=>String(K.name).toLowerCase()===$.toLowerCase());M&&j(`a circuit named "${M.name}" already exists (${M.id}) \u2014 update it instead of making a twin`);let W=g(R.ref,$);return{op:P,...W?{ref:W}:{},name:$,description:ao(R.description,GI,"description"),type:T}}case"circuit.update":{let $=Kg(s,R.circuit);$.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),h($);let T=A(dW),M={};if(T.name!==void 0){let W=ao(T.name,qI,"name");W||j("name can't be blank"),W!==$.name&&(M.name=W)}return T.description!==void 0&&(M.description=ao(T.description,GI,"description")),T.status!==void 0&&(KI.has(T.status)||j(`bad status "${T.status}" (${[...KI].join(" | ")})`),T.status!==$.status&&(M.status=T.status)),T.type!==void 0&&(zd.has(T.type)||j(`bad type "${T.type}" (${[...zd].join(" | ")})`),T.type!==($.type??"build")&&(M.type=T.type)),Object.keys(M).length||j("nothing would change \u2014 the set equals the circuit's current values"),{op:P,circuit:$.id,name:$.name,set:M}}case"circuit.delete":{let $=Kg(s,R.circuit);$.id||j("can't delete a circuit created in the same plan"),h($);let T=o.some((M,W)=>W<I&&Lt(M?.op)==="circuit.update"&&M?.set?.status&&M.set.status!=="active"&&Lt(M?.circuit)===Lt(R.circuit));return $.status==="active"&&!T&&j(`\u201C${$.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:P,circuit:$.id,name:$.name,status:$.status}}case"person.create":{let $=ao(R.label,Gg,"label");$||j("a person row needs a label");let T=a.find(W=>String(W.label).toLowerCase()===$.toLowerCase());T&&j(`a roadmap row labelled "${T.label}" already exists (${T.personId})`);let M=g(R.ref,$);return{op:P,...M?{ref:M}:{},label:$}}case"slot.create":{let $=Kg(s,R.circuit);h($);let T=N(R.person),M=O(R);return{op:P,circuit:$.id??$.ref,circuitName:$.name,person:T.key,personLabel:T.label,...M,estUsd:zI(R.estUsd)}}case"slot.update":{let $=z(R.slot,i,"slotId","slot",l.slots),T=A(pW),M={};if(T.person!==void 0){let K=N(T.person);M.person=K.key,M.personLabel=K.label}if(T.startDate!==void 0||T.endDate!==void 0){let K=O({startDate:T.startDate??$.startDate,endDate:T.endDate??$.endDate});K.startDate!==$.startDate&&(M.startDate=K.startDate),K.endDate!==$.endDate&&(M.endDate=K.endDate)}T.estUsd!==void 0&&(M.estUsd=zI(T.estUsd)),Object.keys(M).length||j("nothing would change");let W=s.find(K=>K.id===$.circuitId);return{op:P,slot:$.slotId,circuitName:W?.name??null,set:M}}case"slot.delete":{let $=z(R.slot,i,"slotId","slot",l.slots),T=s.find(M=>M.id===$.circuitId);return{op:P,slot:$.slotId,circuitName:T?.name??null,startDate:$.startDate,endDate:$.endDate}}case"reservation.create":{let $=N(R.person),T=ao(R.label,Gg,"label");return T||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:P,person:$.key,personLabel:$.label,label:T,...O(R)}}case"reservation.update":{let $=z(R.reservation,c,"reservationId","reservation",l.reservations),T=A(fW),M={};if(T.person!==void 0){let W=N(T.person);M.person=W.key,M.personLabel=W.label}if(T.label!==void 0){let W=ao(T.label,Gg,"label");W||j("label can't be blank"),W!==$.label&&(M.label=W)}if(T.startDate!==void 0||T.endDate!==void 0){let W=O({startDate:T.startDate??$.startDate,endDate:T.endDate??$.endDate});W.startDate!==$.startDate&&(M.startDate=W.startDate),W.endDate!==$.endDate&&(M.endDate=W.endDate)}return Object.keys(M).length||j("nothing would change"),{op:P,reservation:$.reservationId,label:$.label,set:M}}case"reservation.delete":{let $=z(R.reservation,c,"reservationId","reservation",l.reservations);return{op:P,reservation:$.reservationId,label:$.label,startDate:$.startDate,endDate:$.endDate}}default:return j("unhandled op")}});k.forEach((y,I)=>{for(let R of[y.circuit,y.person,y.set?.person])if(Yg(R)&&!d.has(String(R).slice(1)))throw new Error(`op ${I+1} (${y.op}): unknown ref "${R}" \u2014 no earlier op in this plan creates it`)});let x=a.some(y=>y.kind==="user"&&y.userId);return{why:ao(r.why,lW,"why"),ops:k,base:l,resolved:u,snapped:p,createsMeRow:f&&!x}}async function XI(e,t,n,r={}){let o=yW(t,n,r),s=cW(),a={ts:Date.now(),proposalId:s,...o};return await C({id:e.id,cwd:e.cwd},{type:"planning.change.offered",actor:"system",source:"orchestrator",payload:a}),a}var bW=async(e,t)=>{if(!t)throw new Error("proposalId required");let n=await G({id:e.id,cwd:e.cwd}),r=n.find(o=>o.type==="planning.change.offered"&&o.payload?.proposalId===t);if(!r)throw new Error("unknown proposal");if(n.some(o=>o.type==="planning.change.answered"&&o.payload?.proposalId===t))throw new Error("proposal already answered");return r};async function ZI(e,t,n,{results:r=[]}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let o=await bW(e,t),s={id:e.id,cwd:e.cwd},a=o.payload;return n==="applied"&&await C(s,{type:"planning.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,why:a.why??"",results:Array.isArray(r)?r.slice(0,Vg+1):[]}}),await C(s,{type:"planning.change.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,ops:a.ops?.length??0}}),{proposalId:t,action:n}}ju();fe();br();fe();zn();ed();var QI=10,e$=200,kW=120,xW=24,n$=/^\d{4}-\d{2}-\d{2}$/;function Jg(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var vW=(e=Date.now())=>new Date(e).toISOString().slice(0,10),mt=(e,t="i")=>new RegExp(e,t),SW=[{re:mt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:mt("\\bSPC\\b",""),reason:"business (SPC)"},{re:mt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:mt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:mt("\\bReplicas\\b",""),reason:"competitor research"},{re:mt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:mt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:mt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],TW=[mt("\\bSPC\\b",""),mt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),mt("competitor|cloudcli|\\bkiro\\b"),mt("\\bReplicas\\b",""),mt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),mt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),mt("(^|[^\\w&])#\\d+\\b"),mt("\\btrack/[\\w.-]+")];function AW({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of SW)if(o.test(n)||o.test(r))return s;return null}function t$(e){let t=String(e||"");for(let n of TW){let r=n.exec(t);if(r)return r[0].trim()}return null}function r$(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 EW=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function _W(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Xi(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(`
|
|
750
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,kW)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${xW}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=EW.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=t$(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>e$)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${e$}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=t$(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>QI)throw new Error(`${r.length} bullets \u2014 over the ${QI}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function IW(e,{repos:t=Jg()}={}){let n=new Set(t);return(await Vn()).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:AW({title:r.title,headRef:r.headRef})}))}async function o$(e,t){let n=await IW(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function s$(e,{md:t,day:n=null}){let r=_W(t),o=n==null||n===""?vW():String(n);if(!n$.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await C({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 a$(e,{day:t}){let n=String(t||"");if(!n$.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await C({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}}Co();zn();No();fe();Co();ut();er();zn();Ii();Mu();bu();Ni();Sn();import{execFile as FW}from"node:child_process";import{promisify as UW}from"node:util";var $W=new Set(["pull_request","check_suite","workflow_run","push"]),RW=/^[^/\s]+\/[^/\s]+$/,OW=500,Xg=new Set,Zg=[],Yd=new Map,Xt={hourStart:0,byEvent:{},duplicates:0,rejected:0};function Qg(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==Xt.hourStart&&(Xt.hourStart=r,Xt.byEvent={},Xt.duplicates=0,Xt.rejected=0),t==="accepted"?Xt.byEvent[n]=(Xt.byEvent[n]||0)+1:Xt[t]+=1}function CW(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!$W.has(e.event)||typeof e.repo!="string"||!RW.test(e.repo))return null;let t=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(r=>Number.isInteger(r)&&r>0),n=Number.isInteger(e.number)&&e.number>0?e.number:t[0]??null;return{forge:"github",deliveryId:typeof e.deliveryId=="string"&&e.deliveryId?e.deliveryId.slice(0,128):null,event:e.event,action:typeof e.action=="string"?e.action.slice(0,64):null,repo:e.repo,number:n,numbers:n&&!t.includes(n)?[n,...t]:t,headSha:typeof e.headSha=="string"?e.headSha.slice(0,64):null,headRef:typeof e.headRef=="string"?e.headRef.slice(0,256):null,baseRef:typeof e.baseRef=="string"?e.baseRef.slice(0,256):null,merged:typeof e.merged=="boolean"?e.merged:null,draft:typeof e.draft=="boolean"?e.draft:null,conclusion:typeof e.conclusion=="string"?e.conclusion.slice(0,64):null,deleted:e.deleted===!0,senderLogin:typeof e.senderLogin=="string"?e.senderLogin.slice(0,128):null,ts:Number.isFinite(e.ts)?e.ts:null}}function i$(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=CW(e);if(!o)return Qg(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(Xg.has(o.deliveryId))return Qg(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(Xg.add(o.deliveryId),Zg.push(o.deliveryId);Zg.length>OW;)Xg.delete(Zg.shift())}let s=o.repo.toLowerCase(),a=Yd.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Yd.set(s,a),Qg(t,"accepted",o.event),r.log?.(`[github-event] ${o.event}${o.action?` ${o.action}`:""} ${o.repo}${o.number?` #${o.number}`:o.headRef?` ${o.headRef}`:""}${o.conclusion?` (${o.conclusion})`:""}${o.deliveryId?` delivery ${o.deliveryId.slice(0,8)}`:""}`);try{n?.(o)}catch(i){r.error?.("[github-event] onAccept failed:",i?.message||i)}return{ok:!0,duplicate:!1,envelope:o}}function MW(e,t=Date.now()){let n=Yd.get(String(e||"").toLowerCase());return n?{covered:!0,lastDeliveryAt:n.lastDeliveryAt,ageMs:t-n.lastDeliveryAt,count:n.count}:{covered:!1,lastDeliveryAt:null,ageMs:null,count:0}}var c$=1440*6e4;function ey(e,t=Date.now()){let n=MW(e,t);return n.covered&&n.ageMs<c$}function l$(e=Date.now()){let t={};for(let[n,r]of Yd)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<c$};return{hourStart:Xt.hourStart,byEvent:{...Xt.byEvent},duplicates:Xt.duplicates,rejected:Xt.rejected,repos:t}}fe();zn();hr();er();Sn();function u$(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function NW(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var PW=new Set(["merged","closed","lost"]);function DW(e,t){if(!e?.blocked)return null;let n=u$(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 jW(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function BW(e,t,n,r,o){return(await G(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 LW(e,t,n){await C({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 Jd(e,t={}){let{now:n=Date.now(),slugOf:r=_e,lookup:o=st,note:s=Nv,tracks:a=async()=>(await pe()).filter(d=>!Ve(d)),asked:i=BW,emitChange:c=LW}=t,l=[],u=null;for(let d of e??[]){if(!d?.prs?.length)continue;let p=await r(d.path).catch(()=>null);if(p)for(let f of d.prs)try{if(!NW(f))continue;let h=await o(p,f.number).catch(()=>null);if(!h?.trackId||PW.has(h.state))continue;let{prev:w}=await s(p,f.number,{blocked:u$(f),sha:f.headSha??null,at:n});if(!w)continue;let g=DW(w,f);if(!g||jW(h,g.change))continue;u??=new Map((await a()).map(I=>[I.id,I]));let k=u.get(h.trackId);if(!k)continue;let x=f.headSha??null;if(await i(k,g.change,f.number,x,w.at||0))continue;let y={number:Number(f.number),url:f.url||h.url,title:f.title||h.title,branch:f.headRefName||h.headRef,repo:p,sha:x,delegated:!1,reason:g.reason};g.change==="ci-fixed"?y.passed=f.checks.passed:y.base=f.baseRefName||null,await c(k,g.change,y),l.push({change:g.change,number:f.number,reason:g.reason})}catch{}}return l}var ry=UW(FW),g$=3e4,HW=30*6e4,WW=g$,qW=10*6e4,GW=10080*6e4,KW=60*6e4,VW=8,y$=1440*6e4,zW=3,b$="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ny=60*6e4,YW=15*6e4,JW=9e4,XW=1440*6e4,Xd=new Map,Zd=new Map,ZW=/could not resolve|not found|no pull requests?/i,QW=e=>ZW.test(String(e?.message||e||""));function eq(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<ny)}var d$={fast:WW,slow:qW,decay:KW};function p$(e,t,n=ey){return k$(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<HW||eq(e,t))?"fast":t-(e.firstSeen||0)>GW?"decay":"slow"}function Fc(e,t,n,r){let o=e.lanes?.get(ie(t.repo,t.number))||{};return yu({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function tq(e,t){Jn("watch");try{let{stdout:n}=await ry("gh",["pr","view",String(t),"--repo",e,"--json",b$],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await wn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Br(n)&&Lr(n),n}}async function nq(e){Jn("watch");try{let{stdout:t}=await ry("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",b$],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await wn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Br(t)&&Lr(t),t}}async function rq(){let e=new Map;for(let t of await pe().catch(()=>[]))e.set(t.id,t);return e}async function oq(e,t){await C({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}}),await Cu(e,t).catch(()=>{})}async function sq(e,t){let n=await Oo(e);return n?Vs(n,t):null}async function aq(e,t,n){let r=await Oo(e);if(!r)throw new Error(`no local checkout for ${e}`);return zs(r,t,{method:n})}async function iq(e,t){let n=await Oo(e);if(!n)throw new Error(`no local checkout for ${e}`);return Oi(n,t)}async function cq(e,t){await C({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 va(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 lq=10*6e4;async function uq(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>y$){await va(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 va(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await va(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<lq)return;await va(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 va(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 va(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await x$(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Sa(e,t,n){await C({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 dq(e,t){try{return await ry("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Lc(e,t,n,r=()=>!0){return(await G(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Ta=(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 k$(e,t,{grace:n=JW}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=XW}async function pq(e,t,n,r){let o=r.now??Date.now();if(!k$(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ve(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??Mo)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await G(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??Kn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??Kn)(e.repo,e.number).catch(()=>null)?(await Sa(s,"opened",{...Ta(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 fq(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 uu(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await pu(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<YW;await Mv(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Cv(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Lc(a,"pushed",e.number,d=>d.payload?.sha===s)&&await Sa(a,"pushed",{...Ta(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>ny)await du(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 du(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Fc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Lc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Sa(a,"ci-fixed",{...Ta(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await du(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>ny)await fu(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await fu(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Fc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Lc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Sa(a,"reconciled",{...Ta(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await fu(e.repo,e.number).catch(()=>{})}}}}async function mq(e,t,n,r){let o=Fc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Lc(s,"closed",e.number)&&await Sa(s,"closed",{...Ta(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 hq(e,t,n){Fc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Lc(r,"lost",e.number)&&await Sa(r,"lost",Ta(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function x$(e,t,n,r,o={}){let s=Fc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await G(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function f$(e={}){let{now:t=Date.now(),auth:n=$m,view:r=tq,list:o=nq,links:s=jv,trackMap:a=rq,emitMerged:i=oq,mark:c=Dr,archiveIfArmed:l=Zn,notifyIfArmed:u=Qn,notifyIfResolved:d=fd,followupsIfMerged:p=Ko,batchThreshold:f=zW,detail:h=sq,merge:w=aq,updateBranch:g=iq,lookup:k=st,disarm:x=_i,emitDisarmed:y=cq,localHasCommit:I=dq,claim:R=Kn,midTurn:P=Mo,only:H=null,wantDetail:j=null,covered:A=ey,verdicts:O=null}=e;if(!(await n().catch(()=>null))?.authenticated||wu()>0)return 0;let z=await s().catch(()=>[]),$=iS(z),T=Y=>{try{return!!j?.(Y)}catch{return!1}},M=H?z.filter(Y=>{try{return H(Y)}catch{return!1}}):z.filter(Y=>t-(Xd.get(ie(Y.repo,Y.number))||0)>=d$[p$(Y,t,A)]);if(!M.length)return 0;let W=Date.now(),K=new Map,be=new Map;for(let Y of[...M].sort(Ci)){let $e=H?"event":p$(Y,t,A);K.set(ie(Y.repo,Y.number),{lane:$e,intervalMs:d$[$e]??null}),Xd.set(ie(Y.repo,Y.number),t),be.has(Y.repo)||be.set(Y.repo,[]),be.get(Y.repo).push(Y)}let se=await a().catch(()=>new Map),we=new Map,ae=(Y,$e)=>{let Pe=ie(Y,$e);if(!we.has(Pe)){let me=Promise.resolve().then(()=>h(Y,$e));me.catch(()=>{}),we.set(Pe,me)}return we.get(Pe)},V=O??((Y,$e)=>Jd([{path:Y,prs:[$e]}],{now:t,slugOf:async()=>Y,tracks:async()=>[...se.values()].filter(Pe=>!Ve(Pe))}).catch(()=>{})),de={now:t,lanes:K,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:ae,merge:w,updateBranch:g,lookup:k,disarm:x,emitDisarmed:y,localHasCommit:I,claim:R,midTurn:P};for(let[Y,$e]of be){let Pe,me=new Set,_=F=>F.ciFixAwait||F.reconcileAwait||T(F)?!0:!F.autoMerge||F.trackId&&$.get(F.trackId)!==ie(F.repo,F.number)?!1:t-(F.autoMerge.armedAt||0)<=y$;if($e.length>f)Pe=await o(Y).catch(()=>null);else{Pe=new Map;for(let F of $e){if(_(F)){let ge=await ae(Y,F.number).catch(()=>null);if(ge?.state){Pe.set(Number(F.number),{number:Number(F.number),url:ge.url,title:ge.title,headRefName:ge.headRefName,headRefOid:ge.headSha??null,state:ge.state,mergedAt:ge.mergedAt??null,closedAt:ge.closedAt??null});continue}}try{let ge=await r(Y,F.number);ge?.number!=null&&Pe.set(Number(ge.number),ge)}catch(ge){QW(ge)&&me.add(ie(F.repo,F.number))}}}if(Pe)for(let F of $e){let ge=ie(F.repo,F.number),Ne=Pe.get(Number(F.number));if(!Ne){if(me.has(ge)){let Be=(Zd.get(ge)||0)+1;Zd.set(ge,Be),Be>=VW&&(await hq(F,se,de),Zd.delete(ge),Xd.delete(ge))}continue}if(Zd.delete(ge),Ne.state==="MERGED"||Ne.state==="CLOSED")Ne.state==="MERGED"?await x$(F,Ne,se,de):await mq(F,Ne,se,de),Xd.delete(ge);else{await pq(F,Ne,se,de).catch(()=>{}),await fq(F,Ne,se,de).catch(()=>{});let Be=!F.trackId||$.get(F.trackId)===ge;F.autoMerge&&Be&&await uq(F,se,de).catch(()=>{});try{if(T(F)&&!we.has(ge)&&ae(F.repo,F.number),we.has(ge)){let St=await we.get(ge).catch(()=>null);St?.checks&&await V(F.repo,St)}}catch{}}}}let re={};for(let{lane:Y}of K.values())re[Y]=(re[Y]||0)+1;return console.log(`[pr-watch] probed ${M.length} link(s) (${Object.entries(re).map(([Y,$e])=>`${Y} ${$e}`).join(", ")}) in ${Date.now()-W}ms`),M.length}function m$(e,t){return!e||!t?!1:e.all?!0:String(t.repo||"").toLowerCase()!==e.repo?!1:e.numbers?.length?e.numbers.includes(Number(t.number)):e.event==="push"?!!e.branch&&t.headRef===e.branch||!!t.autoMerge:e.branch?t.headRef===e.branch:!0}var ty=!1,Qd=!1,ep=[],h$=Promise.resolve();async function wq(){for(;Qd||ep.length;){if(Qd){Qd=!1;try{await f$()}catch{}continue}let e=ep;ep=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await f$({...n,only:r=>t.some(o=>m$(o,r)),wantDetail:r=>t.some(o=>o.detail&&m$(o,r))})}catch{}}}function tp(e,t){return e===null?Qd=!0:ep.push({spec:e,deps:t}),ty||(ty=!0,h$=wq().finally(()=>{ty=!1})),h$}function v$(e,t=void 0){let n=String(e?.repo||"").toLowerCase();if(!/^[^/\s]+\/[^/\s]+$/.test(n))return Promise.resolve();let r=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(o=>Number.isInteger(o)&&o>0);return tp({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var gq=6e4,w$=0;function S$(e=void 0){let t=e?.now??Date.now();return t-w$<gq?Promise.resolve():(w$=t,tp({all:!0},e))}function T$(){tp(null),setInterval(()=>tp(null),g$)}Co();zm();zu();er();Sn();var yq=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function bq({tracks:e=pe,reconcile:t=Vu}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ve(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function A$(e,t={}){let{invalidate:n=Gt,probe:r=v$,poke:o=HS,reconcileOpened:s=bq}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&yq.has(e.action)){if(e.action==="opened"||e.action==="reopened"){a.poked=!0,a.reconciled=!0;try{o()}catch{}s()}a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event}}else(e.event==="check_suite"||e.event==="workflow_run")&&e.action==="completed"?a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event,detail:!0}:e.event==="push"&&!e.deleted&&(a.probe={repo:e.repo,branch:e.headRef||null,event:"push"});return a.probe&&r(a.probe),a}dt();fe();er();Sn();import{randomUUID as kq}from"node:crypto";var xq=2e4,E$=3,Aa=new Map,oy=new Set;async function _$(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 vq(e={}){let{sessions:t=null,tracks:n=null,append:r=C,read:o=G,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await en(),i=n??await pe()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ve(u)||!u.leadBox)continue;let d=(u.boxes||[]).find(f=>f.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let p=c.get(d.name);if(p)if(l.add(d.name),p.alternateOn){let f=(Aa.get(d.name)||0)+1;Aa.set(d.name,f),f===E$&&(oy.add(d.name),await _$(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:kq(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(Aa.get(d.name)||0)>=E$;if(Aa.delete(d.name),f||!oy.has(d.name)){oy.add(d.name);let h=await _$(u,d.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Aa.keys()])l.has(u)||Aa.delete(u)}var Uc=null,sy=!1;function I$(){return Uc?()=>{}:(Uc=setInterval(()=>{sy||(sy=!0,vq().catch(()=>{}).finally(()=>{sy=!1}))},xq),Uc.unref?.(),()=>{clearInterval(Uc),Uc=null})}_o();zu();fe();Co();No();_o();Li();import{randomUUID as Sq}from"node:crypto";async function Tq(){let e=await Promise.resolve().then(()=>(er(),NE));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var ay={listRepoPrs:Tn,readEvents:G,appendEvent:C,sayDeps:Tq,currentBranch:Fr};async function Aq(e,t=ay,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Fr)(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 iy(e,t,{actions:n,notify:r=!1}={},o=ay){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 Aq(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=Sq();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 cy(e,{offerId:t,choice:n}={},r=ay){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return Wr(`prx:${e.id}:${t}`,()=>Eq(e,{offerId:t,choice:n},r))}async function Eq(e,{offerId:t,choice:n},r){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=Nr.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,Ws(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,xv(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}}Li();import pr from"node:fs/promises";import Ea from"node:path";import P$ from"node:os";import $$ from"node:fs/promises";import np from"node:path";import{execFile as _q}from"node:child_process";import{promisify as Iq}from"node:util";import{fileURLToPath as $q}from"node:url";var Rq=Iq(_q),O$=np.dirname($q(import.meta.url)),Oq=np.resolve(O$,"..","..","client","dist"),Cq="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",Mq=new Map([...Cq].map((e,t)=>[e,t]));function Nq(e){let t=[],n=0,r=0;for(let o of e){let s=Mq.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 Pq(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=Nq(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 Dq(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 R$=new Map;async function jq(e){let t;try{t=await $$.stat(e)}catch{return null}let n=R$.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await $$.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:Pq(r.mappings||"")};return R$.set(e,o),o}catch{return null}}var ly=null;function Bq(){return ly||(ly=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):Rq("git",["rev-parse","--short","HEAD"],{cwd:np.resolve(O$,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),ly}var Lq=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function Fq(e,{distDir:t=Oq}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(Lq)){let[a,i,c,l]=s,u=await jq(np.join(t,"assets",`${i}.map`)),d=u?Dq(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let p=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${p}:${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 C$(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await Bq();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await Fq(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var Uq=()=>process.env.BOSTRAT_STATE_DIR||Ea.join(process.env.HOME||P$.tmpdir(),".bostrat"),io=()=>Ea.join(Uq(),"telemetry"),D$=()=>Ea.join(io(),".relay-offsets.json"),Hq=50,Wq=14,qq=3e4,M$=500,Ft=(e,t)=>typeof e=="string"?e.slice(0,t):"";function Gq(e,t){if(!e||typeof e!="object")return null;let n=Ft(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:Ft(e.anonId,64),sessionId:Ft(e.sessionId,64),userId:Ft(e.userId,64),release:Ft(e.release,64),appVersion:Ft(e.appVersion,32),route:Ft(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:Ft(e.error.kind,256),message:Ft(e.error.message,8192),stack:Ft(e.error.stack,32768),fingerprint:Ft(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:Ft(s?.n,64),r:Ft(s?.r,128)}))),o}var j$=e=>new Date(e).toISOString().slice(0,10),N$=0;async function B$(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,Hq):[]).map(s=>Gq(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await C$(s);await pr.mkdir(io(),{recursive:!0});let o=Ea.join(io(),`${j$(t)}.ndjson`);return await pr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
751
|
-
`).join(""),"utf8"),t-
|
|
752
|
-
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=M$){let f=d.slice(p,p+M$).map(w=>Vq(w)),h=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!h||!h.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await U$(r)}return{sent:o,disabled:!1}}var uy=null,dy=!1;function H$(){uy||(uy=setInterval(async()=>{if(!dy){dy=!0;try{await zq()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{dy=!1}}},qq),uy.unref?.())}import sp from"node:fs/promises";import V$ from"node:path";import Yq from"node:os";var z$=()=>process.env.BOSTRAT_STATE_DIR||V$.join(process.env.HOME||Yq.tmpdir(),".bostrat"),Y$=()=>V$.join(z$(),"dictation-glossary.json"),Jq=100,Xq=500,Zq=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],ap=null,py=null,fy=Promise.resolve();function W$(){let e={},t=Date.now();for(let n of Zq)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function J$(){return ap||(py??=sp.readFile(Y$(),"utf8").then(e=>JSON.parse(e)?.terms??W$()).catch(()=>W$()).then(e=>ap=e).finally(()=>py=null),py)}async function q$(){let e=JSON.stringify({terms:ap},null,2),t=Y$(),n=`${t}.${process.pid}.tmp`;await sp.mkdir(z$(),{recursive:!0}),await sp.writeFile(n,e),await sp.rename(n,t)}function Qq(){return fy=fy.then(q$,q$),fy.catch(()=>{})}async function my(){return Object.values(await J$()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var e5=e=>Math.max(1,Math.ceil(e.length/4));async function t5(){let e=[],t=0;for(let{term:n}of await my()){let r=e5(n);if(!(t+r>Xq)&&(e.push(n),t+=r,e.length>=Jq))break}return e}async function n5(e,t){let n=await J$(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var G$=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),op=e=>e.toLowerCase();function K$(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function X$(e,t){let n=G$(e),r=G$(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]=op(n[u])===op(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;)op(n[c])===op(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(K$(r[l])&&i.add(r[l]),l++);for(;l<s;l++)K$(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await n5(u,"learned");return await Qq(),[...i]}var Hc=null;function r5(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 Z$(){if(!Hc)return null;let e=await Sc();if(!e||e.apiKey.slice(-6)!==Hc.keyTail)return null;let{keyTail:t,...n}=Hc;return n}async function Q$(e,t){let n=await Sc();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 t5())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=r5(o.status,i);c.persistent&&(Hc={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 Hc=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}fe();var hy={"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 o5(e){if(!e)return null;let t=String(e).trim();if(hy[t])return t;let n=null;for(let r of Object.keys(hy))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function s5(e,t={}){let n=hy[o5(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 a5(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 Wc=null;async function i5(){if(!Wc)return null;let e=await Od();if(!e||e.apiKey.slice(-6)!==Wc.keyTail)return null;let{keyTail:t,...n}=Wc;return n}async function c5(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=a5(t.status,o);s.persistent&&(Wc={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}Wc=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 l5={openai:c5};async function eR(){let e=await Od();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await i5()}:{configured:!1}}async function tR(){let e=await Od();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 l5[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function nR(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=s5(t,s),i=await C(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}}Sn();var uR=Ae.dirname(d5(import.meta.url)),rR=Number(process.env.PORT)||3362,cp=Zw(process.env),dR=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,_a=gI(process.env.BOSTRAT_ALLOW_IPS),ky=yI(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Vd),xy=kI(),pR=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",S=Ut();S.disable("x-powered-by");S.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Kd(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});S.use((e,t,n)=>{if(jc(e)||_a.allowed(_a.clientIp(e))||ky.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(EI);S.use(SI({token:xy,enforce:pR}));S.use("/api/mocks",Ut.json({limit:"1mb"}));S.use("/api/mocks",Ut.raw({type:"image/png",limit:"24mb"}));S.use("/api/mocks",Ut.raw({type:["video/mp4","video/webm"],limit:"105mb"}));S.use("/api/runbooks",Ut.json({limit:"1mb"}));S.use("/api/tracks/adopt",Ut.json({limit:"1mb"}));S.use("/api/telemetry",Ut.json({limit:"256kb"}));S.use("/api/hook",Ut.json({limit:"2mb"}));S.use("/api/dictation",Ut.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",Ut.raw({type:()=>!0,limit:"25mb"}));S.use(Ut.json({limit:"64kb"}));S.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:Rt,hostTmuxAlive:await xr(),autoTriggerPct:dR,plane:Ia?Ia.status():null})});S.get("/api/sessions",async(e,t)=>{t.json({sessions:await en()})});S.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await vr(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});S.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Fe(n))return t.status(400).json({error:"invalid session name"});if(!await ve(n))return t.status(404).json({error:"session not found"});try{await tn(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});S.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Fe(n))return t.status(400).json({error:"invalid session name"});let r=await ve(n);if(!r)return t.status(404).json({error:"session not found"});let o=await ri(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});S.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Fe(n))return t.status(400).json({error:"invalid session name"});try{let r=await j0(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});S.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=Vl(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 oR=10;S.get("/api/idle-guard",async(e,t)=>{try{t.json(Xw({tracks:await pe(),views:await qu(),queuedOf:n=>Ho(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Zt=zo(),g5=zE({kind:Zt}),qc=null,vt=()=>qc&&["auto","prompt","off"].includes(qc)?qc:g5,by=null,y5=xc.has(Zt),fR=async({to:e,from:t})=>!y5||Td(await vc())?!1:(Kc.arm({to:e,from:t,kind:Zt}),!0);async function b5(e){let t=vt();qc=e??null;let n=vt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Kc.cancel();return}by&&await fR(by)}}var mR=(()=>{try{return w5(import.meta.url)("../package.json").version||null}catch{return null}})(),Kc=JE({isBusy:async()=>Xw({tracks:await pe(),views:await qu(),queuedOf:e=>Ho(e)}).busy,onStuck:({to:e,from:t})=>Nn({current:t,latest:e,installKind:Zt,policy:vt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Nn({current:t,latest:e,installKind:Zt,policy:vt(),status:"updating",reason:"auto"}):Nn({current:t,latest:e,installKind:Zt,policy:vt(),status:"failed",error:n?.reason})).catch(()=>{})});S.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Zt,policy:vt(),policySource:qc?"plane":"node",current:mR,status:await vc(),armed:Kc.armed})});S.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Ad({to:n,kind:Zt,reason:"manual"});if(r.ok)return Kc.cancel(),Nn({current:r.from,latest:n,installKind:Zt,policy:vt(),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 hR(){try{let e=await YE();e&&await Nn({current:e.from,latest:e.to,installKind:e.kind||Zt,policy:vt(),status:e.status,error:e.error,reason:e.reason}),await ww({current:mR,installKind:Zt,policy:vt()})}catch{}}hR();setInterval(hR,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await pe()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>oR){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,oR).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:uc,agentAvailability:await id(),tickets:await v_()})});S.get("/api/cost",async(e,t)=>{try{let n=await pe();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Mg(n);if(r.cursorUsage=ph(),e.query.format==="text")return t.type("text/plain").send(V_(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/roundup",async(e,t)=>{try{let n=await Pg(await pe(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(Z_(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/spend",async(e,t)=>{try{let n=await rI(await pe(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=ph(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Ti(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(Ti())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await ou(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});S.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await pI(await pe(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Py(e.get(Bn),{byToken:pc,legacy:()=>Hi(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Pa("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Pa("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&&Pa("mismatch",{track:r.track.id,box:r.box.name}),Pa(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 q_(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&dl(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await pc(e.get(Bn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await _S(n.track,n.box)).join(`
|
|
753
|
+
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function h$(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${hq(n.host)}/`}function hq(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 np(t)&&Lh(n[0])?n.slice(1).join("."):t}ca();Xh();ld();uh();de();import w$ from"node:os";import{randomUUID as cy}from"node:crypto";var wq=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||w$.hostname(),nodeName:e.BOSTRAT_NODE_NAME||w$.hostname()});var g$=32768,y$=120,b$=1e3,k$=new Set(["claude","codex","cursor"]),gq=new Set(["off","transition","nag"]),yq=[[0,59],[0,23],[1,31],[1,12],[0,6]];function bq(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=yq[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 mn=e=>String(e??"").trim(),cs={name(e){let t=mn(e);if(!t)throw new Error("a loop needs a name");if(t.length>y$)throw new Error(`name is too long (${t.length}); cap is ${y$}`);return t},schedule(e){let t=mn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!bq(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=mn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>g$)throw new Error(`prompt is too long (${t.length} chars); cap is ${g$}`);return t},expectation(e){let t=mn(e)||null;if(t&&t.length>b$)throw new Error(`expectation is too long (${t.length}); cap is ${b$}`);return t},notify(e){let t=mn(e)||"off";if(!gq.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=mn(typeof t=="string"?t:t?.provider)||"claude";if(!k$.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...k$].join(", ")}`);let r=typeof t=="object"&&t&&mn(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 kq(e,t){let n=e&&typeof e=="object"?e:{},r=cs.name(n.name),o=cs.schedule(n.schedule),s=cs.prompt(n.prompt),a=cs.expectation(n.expectation),i=cs.notify(n.notify),c=cs.assignment(n.assignment),l=mn(t?.workspace?.repo)||mn(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}=wq();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function v$(e,t){let n=kq(t,e),r=cy();return await C({id:e.id,cwd:e.cwd},{type:"loop.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:r,...n}}),{proposalId:r,...n}}async function S$(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await K({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 C({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 C({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 C({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 iy=["name","schedule","prompt","expectation","notify","assignment","enabled"],xq=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function T$(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function A$(e,t){let n=mn(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 vq(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=>!iy.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: ${iy.join(", ")}`)}let o={};for(let a of iy){if(!(a in n))continue;let i=cs[a](n[a]);(a==="assignment"?xq(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function E$(e,t,n){let r=T$(t),{patch:o,changed:s}=vq(n,r),a=cy(),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 C({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var x$=600;async function _$(e,t,n){let r=mn(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>x$)throw new Error(`reason is too long (${r.length}); cap is ${x$}`);let o=T$(t),s=cy(),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 C({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var I$=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await K({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function $$(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await I$(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await C(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 C(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function R$(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await I$(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await C(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 C(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}}de();import{randomUUID as Sq}from"node:crypto";var dy=20,Tq=300,ly=120,O$=120,C$=2e3,op=new Set(["build","ops","debt","spike"]),M$=new Set(["active","done","abandoned"]),py=new Set(["circuit.create","circuit.update","circuit.delete"]),Aq=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),zc=new Set([...py,...Aq]),Eq=["name","description","status","type"],_q=["person","startDate","endDate","estUsd"],Iq=["person","label","startDate","endDate"],$q=/^\d{4}-\d{2}-\d{2}$/,Lt=e=>String(e??"").trim(),fy=e=>typeof e=="string"&&e.startsWith("@"),D$=864e5;function j$(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*D$).toISOString().slice(0,10)}function Rq(e){return new Date(Date.parse(`${j$(e)}T00:00:00Z`)+6*D$).toISOString().slice(0,10)}var N$=(e,t)=>{let n=Lt(e);if(!$q.test(n))throw new Error(`${t} must be YYYY-MM-DD, got "${e??""}"`);if(!Number.isFinite(Date.parse(`${n}T00:00:00Z`)))throw new Error(`${t} "${n}" is not a real date`);return n};function Oq(e,t){let n=N$(e,"startDate"),r=N$(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=j$(n),s=Rq(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var P$=e=>{if(e==null||e==="")return null;let t=Number(e);if(!Number.isFinite(t)||t<0)throw new Error(`estUsd must be a non-negative number, got "${e}"`);return t},fo=(e,t,n)=>{let r=Lt(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function uy(e,t){let n=Lt(t);if(!n)throw new Error("a circuit name or id is required");if(fy(n))return{ref:n,id:null,name:n.slice(1)};let r=e.find(i=>i.id===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(i=>String(i.name).toLowerCase()===o);if(s.length===1)return s[0];if(s.length>1)throw new Error(`"${n}" names ${s.length} circuits \u2014 use an id (bostrat-circuit ls)`);let a=e.filter(i=>String(i.name).toLowerCase().includes(o)).slice(0,5);throw new Error(a.length?`no circuit named "${n}" \u2014 near matches: ${a.map(i=>`${i.name} (${i.id})`).join(", ")}`:`no circuit named "${n}" \u2014 propose a circuit.create op if it should exist (bostrat-circuit ls lists yours)`)}function Cq(e,t){let n=Lt(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||fy(n))return{ref:n,id:null,label:n==="me"?"you":n.slice(1)};let r=e.find(a=>a.personId===n);if(r)return{...r,id:r.personId};let o=n.toLowerCase(),s=e.filter(a=>String(a.label).toLowerCase()===o);if(s.length===1)return{...s[0],id:s[0].personId};throw s.length>1?new Error(`"${n}" names ${s.length} roadmap rows \u2014 use a person id`):new Error(e.length?`no roadmap row for "${n}" \u2014 rows: ${e.map(a=>`${a.label} (${a.personId})`).join(", ")}, or propose a person.create op`:`no roadmap rows yet \u2014 use "me", or propose a person.create op for "${n}"`)}function Mq(e,t,{allow:n=zc}={}){let r=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!r)throw new Error("a plan must be a JSON object with an `ops` array");let o=Array.isArray(r.ops)?r.ops:null;if(!o?.length)throw new Error("a plan needs at least one op in `ops`");if(o.length>dy)throw new Error(`too many ops (${o.length}); cap is ${dy} \u2014 propose one intent at a time`);let s=Array.isArray(t?.circuits)?t.circuits:[],a=Array.isArray(t?.people)?t.people:[],i=Array.isArray(t?.slots)?t.slots:[],c=Array.isArray(t?.reservations)?t.reservations:[],l={circuits:{},slots:{},reservations:{}},u={},d=new Set,p=!1,f=!1,w=y=>{y.id&&(l.circuits[y.id]={name:y.name,status:y.status,type:y.type??"build",updatedAt:y.updatedAt??null},u[y.id]=y.name)},m=y=>{y.id&&(u[y.id]=y.label)},g=(y,$)=>{let O=Lt(y);if(!O)return null;if(d.has(O))throw new Error(`ref "${O}" is used by two ops \u2014 each ref names one thing`);return d.add(O),u[`@${O}`]=$,O},b=o.map((y,$)=>{let O=y&&typeof y=="object"?y:{},M=Lt(O.op),H=`op ${$+1}`;if(!zc.has(M))throw new Error(`${H}: unknown op "${M}" \u2014 one of ${[...zc].join(", ")}`);if(!n.has(M))throw new Error(`${H}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let P=A=>{throw new Error(`${H} (${M}): ${A}`)},E=A=>{let _=O.set&&typeof O.set=="object"&&!Array.isArray(O.set)?O.set:null;_||P("needs a `set` object naming the fields to change");let N=Object.keys(_).filter(U=>!A.includes(U));return N.length&&P(`unknown field(s) ${N.join(", ")} \u2014 settable: ${A.join(", ")}`),Object.keys(_).length||P("`set` names no fields"),_},R=A=>{let{startDate:_,endDate:N,snapped:U}=Oq(A.startDate,A.endDate);return U&&(p=!0),{startDate:_,endDate:N,snapped:U}},G=A=>{let _=Cq(a,A);return Lt(A)==="me"&&(f=!0),m(_),{key:_.id??Lt(A),label:_.label}},V=(A,_,N,U,z)=>{let ue=Lt(A);ue||P(`needs a ${U} id`);let pe=_.find(Se=>Se[N]===ue);return pe||P(`no ${U} "${ue}" \u2014 see \`bostrat-roadmap ls\``),z[ue]={...pe},pe};switch(M){case"circuit.create":{let A=fo(O.name,O$,"name");A||P("a circuit needs a name");let _=Lt(O.type)||"build";op.has(_)||P(`bad type "${_}" (${[...op].join(" | ")})`);let N=s.find(z=>String(z.name).toLowerCase()===A.toLowerCase());N&&P(`a circuit named "${N.name}" already exists (${N.id}) \u2014 update it instead of making a twin`);let U=g(O.ref,A);return{op:M,...U?{ref:U}:{},name:A,description:fo(O.description,C$,"description"),type:_}}case"circuit.update":{let A=uy(s,O.circuit);A.id||P("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(A);let _=E(Eq),N={};if(_.name!==void 0){let U=fo(_.name,O$,"name");U||P("name can't be blank"),U!==A.name&&(N.name=U)}return _.description!==void 0&&(N.description=fo(_.description,C$,"description")),_.status!==void 0&&(M$.has(_.status)||P(`bad status "${_.status}" (${[...M$].join(" | ")})`),_.status!==A.status&&(N.status=_.status)),_.type!==void 0&&(op.has(_.type)||P(`bad type "${_.type}" (${[...op].join(" | ")})`),_.type!==(A.type??"build")&&(N.type=_.type)),Object.keys(N).length||P("nothing would change \u2014 the set equals the circuit's current values"),{op:M,circuit:A.id,name:A.name,set:N}}case"circuit.delete":{let A=uy(s,O.circuit);A.id||P("can't delete a circuit created in the same plan"),w(A);let _=o.some((N,U)=>U<$&&Lt(N?.op)==="circuit.update"&&N?.set?.status&&N.set.status!=="active"&&Lt(N?.circuit)===Lt(O.circuit));return A.status==="active"&&!_&&P(`\u201C${A.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:A.id,name:A.name,status:A.status}}case"person.create":{let A=fo(O.label,ly,"label");A||P("a person row needs a label");let _=a.find(U=>String(U.label).toLowerCase()===A.toLowerCase());_&&P(`a roadmap row labelled "${_.label}" already exists (${_.personId})`);let N=g(O.ref,A);return{op:M,...N?{ref:N}:{},label:A}}case"slot.create":{let A=uy(s,O.circuit);w(A);let _=G(O.person),N=R(O);return{op:M,circuit:A.id??A.ref,circuitName:A.name,person:_.key,personLabel:_.label,...N,estUsd:P$(O.estUsd)}}case"slot.update":{let A=V(O.slot,i,"slotId","slot",l.slots),_=E(_q),N={};if(_.person!==void 0){let z=G(_.person);N.person=z.key,N.personLabel=z.label}if(_.startDate!==void 0||_.endDate!==void 0){let z=R({startDate:_.startDate??A.startDate,endDate:_.endDate??A.endDate});z.startDate!==A.startDate&&(N.startDate=z.startDate),z.endDate!==A.endDate&&(N.endDate=z.endDate)}_.estUsd!==void 0&&(N.estUsd=P$(_.estUsd)),Object.keys(N).length||P("nothing would change");let U=s.find(z=>z.id===A.circuitId);return{op:M,slot:A.slotId,circuitName:U?.name??null,set:N}}case"slot.delete":{let A=V(O.slot,i,"slotId","slot",l.slots),_=s.find(N=>N.id===A.circuitId);return{op:M,slot:A.slotId,circuitName:_?.name??null,startDate:A.startDate,endDate:A.endDate}}case"reservation.create":{let A=G(O.person),_=fo(O.label,ly,"label");return _||P("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:A.key,personLabel:A.label,label:_,...R(O)}}case"reservation.update":{let A=V(O.reservation,c,"reservationId","reservation",l.reservations),_=E(Iq),N={};if(_.person!==void 0){let U=G(_.person);N.person=U.key,N.personLabel=U.label}if(_.label!==void 0){let U=fo(_.label,ly,"label");U||P("label can't be blank"),U!==A.label&&(N.label=U)}if(_.startDate!==void 0||_.endDate!==void 0){let U=R({startDate:_.startDate??A.startDate,endDate:_.endDate??A.endDate});U.startDate!==A.startDate&&(N.startDate=U.startDate),U.endDate!==A.endDate&&(N.endDate=U.endDate)}return Object.keys(N).length||P("nothing would change"),{op:M,reservation:A.reservationId,label:A.label,set:N}}case"reservation.delete":{let A=V(O.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:A.reservationId,label:A.label,startDate:A.startDate,endDate:A.endDate}}default:return P("unhandled op")}});b.forEach((y,$)=>{for(let O of[y.circuit,y.person,y.set?.person])if(fy(O)&&!d.has(String(O).slice(1)))throw new Error(`op ${$+1} (${y.op}): unknown ref "${O}" \u2014 no earlier op in this plan creates it`)});let x=a.some(y=>y.kind==="user"&&y.userId);return{why:fo(r.why,Tq,"why"),ops:b,base:l,resolved:u,snapped:p,createsMeRow:f&&!x}}async function B$(e,t,n,r={}){let o=Mq(t,n,r),s=Sq(),a={ts:Date.now(),proposalId:s,...o};return await C({id:e.id,cwd:e.cwd},{type:"planning.change.offered",actor:"system",source:"orchestrator",payload:a}),a}var Nq=async(e,t)=>{if(!t)throw new Error("proposalId required");let n=await K({id:e.id,cwd:e.cwd}),r=n.find(o=>o.type==="planning.change.offered"&&o.payload?.proposalId===t);if(!r)throw new Error("unknown proposal");if(n.some(o=>o.type==="planning.change.answered"&&o.payload?.proposalId===t))throw new Error("proposal already answered");return r};async function L$(e,t,n,{results:r=[]}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let o=await Nq(e,t),s={id:e.id,cwd:e.cwd},a=o.payload;return n==="applied"&&await C(s,{type:"planning.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,why:a.why??"",results:Array.isArray(r)?r.slice(0,dy+1):[]}}),await C(s,{type:"planning.change.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,ops:a.ops?.length??0}}),{proposalId:t,action:n}}zu();de();Tr();de();Zn();ld();var F$=10,U$=200,Pq=120,Dq=24,W$=/^\d{4}-\d{2}-\d{2}$/;function my(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var jq=(e=Date.now())=>new Date(e).toISOString().slice(0,10),ht=(e,t="i")=>new RegExp(e,t),Bq=[{re:ht("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:ht("\\bSPC\\b",""),reason:"business (SPC)"},{re:ht("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:ht("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:ht("\\bReplicas\\b",""),reason:"competitor research"},{re:ht("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:ht("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:ht("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],Lq=[ht("\\bSPC\\b",""),ht("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),ht("competitor|cloudcli|\\bkiro\\b"),ht("\\bReplicas\\b",""),ht("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),ht("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),ht("(^|[^\\w&])#\\d+\\b"),ht("\\btrack/[\\w.-]+")];function Fq({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of Bq)if(o.test(n)||o.test(r))return s;return null}function H$(e){let t=String(e||"");for(let n of Lq){let r=n.exec(t);if(r)return r[0].trim()}return null}function q$(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 Uq=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function Hq(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=ic(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(`
|
|
754
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,Pq)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${Dq}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=Uq.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=H$(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>U$)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${U$}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=H$(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>F$)throw new Error(`${r.length} bullets \u2014 over the ${F$}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function Wq(e,{repos:t=my()}={}){let n=new Set(t);return(await Xn()).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:Fq({title:r.title,headRef:r.headRef})}))}async function K$(e,t){let n=await Wq(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function G$(e,{md:t,day:n=null}){let r=Hq(t),o=n==null||n===""?jq():String(n);if(!W$.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await C({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 z$(e,{day:t}){let n=String(t||"");if(!W$.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await C({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}}Bo();Zn();Fo();de();Bo();pt();or();Zn();Ni();Wu();$u();Fi();An();import{execFile as t5}from"node:child_process";import{promisify as n5}from"node:util";var qq=new Set(["pull_request","check_suite","workflow_run","push"]),Kq=/^[^/\s]+\/[^/\s]+$/,Gq=500,hy=new Set,wy=[],sp=new Map,Yt={hourStart:0,byEvent:{},duplicates:0,rejected:0};function gy(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==Yt.hourStart&&(Yt.hourStart=r,Yt.byEvent={},Yt.duplicates=0,Yt.rejected=0),t==="accepted"?Yt.byEvent[n]=(Yt.byEvent[n]||0)+1:Yt[t]+=1}function zq(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!qq.has(e.event)||typeof e.repo!="string"||!Kq.test(e.repo))return null;let t=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(r=>Number.isInteger(r)&&r>0),n=Number.isInteger(e.number)&&e.number>0?e.number:t[0]??null;return{forge:"github",deliveryId:typeof e.deliveryId=="string"&&e.deliveryId?e.deliveryId.slice(0,128):null,event:e.event,action:typeof e.action=="string"?e.action.slice(0,64):null,repo:e.repo,number:n,numbers:n&&!t.includes(n)?[n,...t]:t,headSha:typeof e.headSha=="string"?e.headSha.slice(0,64):null,headRef:typeof e.headRef=="string"?e.headRef.slice(0,256):null,baseRef:typeof e.baseRef=="string"?e.baseRef.slice(0,256):null,merged:typeof e.merged=="boolean"?e.merged:null,draft:typeof e.draft=="boolean"?e.draft:null,conclusion:typeof e.conclusion=="string"?e.conclusion.slice(0,64):null,deleted:e.deleted===!0,senderLogin:typeof e.senderLogin=="string"?e.senderLogin.slice(0,128):null,ts:Number.isFinite(e.ts)?e.ts:null}}function V$(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=zq(e);if(!o)return gy(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(hy.has(o.deliveryId))return gy(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(hy.add(o.deliveryId),wy.push(o.deliveryId);wy.length>Gq;)hy.delete(wy.shift())}let s=o.repo.toLowerCase(),a=sp.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,sp.set(s,a),gy(t,"accepted",o.event),r.log?.(`[github-event] ${o.event}${o.action?` ${o.action}`:""} ${o.repo}${o.number?` #${o.number}`:o.headRef?` ${o.headRef}`:""}${o.conclusion?` (${o.conclusion})`:""}${o.deliveryId?` delivery ${o.deliveryId.slice(0,8)}`:""}`);try{n?.(o)}catch(i){r.error?.("[github-event] onAccept failed:",i?.message||i)}return{ok:!0,duplicate:!1,envelope:o}}function Vq(e,t=Date.now()){let n=sp.get(String(e||"").toLowerCase());return n?{covered:!0,lastDeliveryAt:n.lastDeliveryAt,ageMs:t-n.lastDeliveryAt,count:n.count}:{covered:!1,lastDeliveryAt:null,ageMs:null,count:0}}var J$=1440*6e4;function yy(e,t=Date.now()){let n=Vq(e,t);return n.covered&&n.ageMs<J$}function Y$(e=Date.now()){let t={};for(let[n,r]of sp)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<J$};return{hourStart:Yt.hourStart,byEvent:{...Yt.byEvent},duplicates:Yt.duplicates,rejected:Yt.rejected,repos:t}}de();Zn();kr();or();An();function X$(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function Jq(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var Yq=new Set(["merged","closed","lost"]);function Xq(e,t){if(!e?.blocked)return null;let n=X$(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 Zq(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function Qq(e,t,n,r,o){return(await K(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 e5(e,t,n){await C({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 ap(e,t={}){let{now:n=Date.now(),slugOf:r=_e,lookup:o=at,note:s=rS,tracks:a=async()=>(await me()).filter(d=>!Ve(d)),asked:i=Qq,emitChange:c=e5}=t,l=[],u=null;for(let d of e??[]){if(!d?.prs?.length)continue;let p=await r(d.path).catch(()=>null);if(p)for(let f of d.prs)try{if(!Jq(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||Yq.has(w.state))continue;let{prev:m}=await s(p,f.number,{blocked:X$(f),sha:f.headSha??null,at:n});if(!m)continue;let g=Xq(m,f);if(!g||Zq(w,g.change))continue;u??=new Map((await a()).map($=>[$.id,$]));let b=u.get(w.trackId);if(!b)continue;let x=f.headSha??null;if(await i(b,g.change,f.number,x,m.at||0))continue;let y={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:x,delegated:!1,reason:g.reason};g.change==="ci-fixed"?y.passed=f.checks.passed:y.base=f.baseRefName||null,await c(b,g.change,y),l.push({change:g.change,number:f.number,reason:g.reason})}catch{}}return l}var xy=n5(t5),oR=3e4,r5=30*6e4,o5=oR,s5=10*6e4,a5=10080*6e4,i5=60*6e4,c5=8,sR=1440*6e4,l5=3,aR="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ky=60*6e4,u5=15*6e4,d5=9e4,p5=1440*6e4,ip=new Map,cp=new Map,f5=/could not resolve|not found|no pull requests?/i,m5=e=>f5.test(String(e?.message||e||""));function h5(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<ky)}var Z$={fast:o5,slow:s5,decay:i5};function Q$(e,t,n=yy){return iR(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<r5||h5(e,t))?"fast":t-(e.firstSeen||0)>a5?"decay":"slow"}function Jc(e,t,n,r){let o=e.lanes?.get(se(t.repo,t.number))||{};return Iu({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function w5(e,t){er("watch");try{let{stdout:n}=await xy("gh",["pr","view",String(t),"--repo",e,"--json",aR],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await yn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw qr(n)&&Kr(n),n}}async function g5(e){er("watch");try{let{stdout:t}=await xy("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",aR],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await yn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw qr(t)&&Kr(t),t}}async function y5(){let e=new Map;for(let t of await me().catch(()=>[]))e.set(t.id,t);return e}async function b5(e,t){await C({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}}),await Hu(e,t).catch(()=>{})}async function k5(e,t){let n=await jo(e);return n?Xs(n,t):null}async function x5(e,t,n){let r=await jo(e);if(!r)throw new Error(`no local checkout for ${e}`);return Zs(r,t,{method:n})}async function v5(e,t){let n=await jo(e);if(!n)throw new Error(`no local checkout for ${e}`);return ji(n,t)}async function S5(e,t){await C({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 Ia(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 T5=10*6e4;async function A5(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>sR){await Ia(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 Ia(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Ia(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<T5)return;await Ia(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 Ia(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 Ia(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await cR(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function $a(e,t,n){await C({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 E5(e,t){try{return await xy("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Vc(e,t,n,r=()=>!0){return(await K(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Ra=(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 iR(e,t,{grace:n=d5}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=p5}async function _5(e,t,n,r){let o=r.now??Date.now();if(!iR(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ve(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??Lo)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await K(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??Yn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??Yn)(e.repo,e.number).catch(()=>null)?(await $a(s,"opened",{...Ra(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 I5(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 ku(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await vu(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<u5;await nS(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await tS(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Vc(a,"pushed",e.number,d=>d.payload?.sha===s)&&await $a(a,"pushed",{...Ra(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>ky)await xu(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 xu(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Jc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Vc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await $a(a,"ci-fixed",{...Ra(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await xu(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>ky)await Su(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Su(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Jc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Vc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await $a(a,"reconciled",{...Ra(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Su(e.repo,e.number).catch(()=>{})}}}}async function $5(e,t,n,r){let o=Jc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Vc(s,"closed",e.number)&&await $a(s,"closed",{...Ra(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 R5(e,t,n){Jc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Vc(r,"lost",e.number)&&await $a(r,"lost",Ra(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function cR(e,t,n,r,o={}){let s=Jc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await K(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function eR(e={}){let{now:t=Date.now(),auth:n=Lm,view:r=w5,list:o=g5,links:s=aS,trackMap:a=y5,emitMerged:i=b5,mark:c=Hr,archiveIfArmed:l=nr,notifyIfArmed:u=rr,notifyIfResolved:d=Sd,followupsIfMerged:p=Xo,batchThreshold:f=l5,detail:w=k5,merge:m=x5,updateBranch:g=v5,lookup:b=at,disarm:x=Mi,emitDisarmed:y=S5,localHasCommit:$=E5,claim:O=Yn,midTurn:M=Lo,only:H=null,wantDetail:P=null,covered:E=yy,verdicts:R=null}=e;if(!(await n().catch(()=>null))?.authenticated||Eu()>0)return 0;let V=await s().catch(()=>[]),A=RS(V),_=J=>{try{return!!P?.(J)}catch{return!1}},N=H?V.filter(J=>{try{return H(J)}catch{return!1}}):V.filter(J=>t-(ip.get(se(J.repo,J.number))||0)>=Z$[Q$(J,t,E)]);if(!N.length)return 0;let U=Date.now(),z=new Map,ue=new Map;for(let J of[...N].sort(Bi)){let ge=H?"event":Q$(J,t,E);z.set(se(J.repo,J.number),{lane:ge,intervalMs:Z$[ge]??null}),ip.set(se(J.repo,J.number),t),ue.has(J.repo)||ue.set(J.repo,[]),ue.get(J.repo).push(J)}let pe=await a().catch(()=>new Map),Se=new Map,Ae=(J,ge)=>{let Oe=se(J,ge);if(!Se.has(Oe)){let Me=Promise.resolve().then(()=>w(J,ge));Me.catch(()=>{}),Se.set(Oe,Me)}return Se.get(Oe)},W=R??((J,ge)=>ap([{path:J,prs:[ge]}],{now:t,slugOf:async()=>J,tracks:async()=>[...pe.values()].filter(Oe=>!Ve(Oe))}).catch(()=>{})),te={now:t,lanes:z,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Ae,merge:m,updateBranch:g,lookup:b,disarm:x,emitDisarmed:y,localHasCommit:$,claim:O,midTurn:M};for(let[J,ge]of ue){let Oe,Me=new Set,I=j=>j.ciFixAwait||j.reconcileAwait||_(j)?!0:!j.autoMerge||j.trackId&&A.get(j.trackId)!==se(j.repo,j.number)?!1:t-(j.autoMerge.armedAt||0)<=sR;if(ge.length>f)Oe=await o(J).catch(()=>null);else{Oe=new Map;for(let j of ge){if(I(j)){let fe=await Ae(J,j.number).catch(()=>null);if(fe?.state){Oe.set(Number(j.number),{number:Number(j.number),url:fe.url,title:fe.title,headRefName:fe.headRefName,headRefOid:fe.headSha??null,state:fe.state,mergedAt:fe.mergedAt??null,closedAt:fe.closedAt??null});continue}}try{let fe=await r(J,j.number);fe?.number!=null&&Oe.set(Number(fe.number),fe)}catch(fe){m5(fe)&&Me.add(se(j.repo,j.number))}}}if(Oe)for(let j of ge){let fe=se(j.repo,j.number),Pe=Oe.get(Number(j.number));if(!Pe){if(Me.has(fe)){let Ce=(cp.get(fe)||0)+1;cp.set(fe,Ce),Ce>=c5&&(await R5(j,pe,te),cp.delete(fe),ip.delete(fe))}continue}if(cp.delete(fe),Pe.state==="MERGED"||Pe.state==="CLOSED")Pe.state==="MERGED"?await cR(j,Pe,pe,te):await $5(j,Pe,pe,te),ip.delete(fe);else{await _5(j,Pe,pe,te).catch(()=>{}),await I5(j,Pe,pe,te).catch(()=>{});let Ce=!j.trackId||A.get(j.trackId)===fe;j.autoMerge&&Ce&&await A5(j,pe,te).catch(()=>{});try{if(_(j)&&!Se.has(fe)&&Ae(j.repo,j.number),Se.has(fe)){let wn=await Se.get(fe).catch(()=>null);wn?.checks&&await W(j.repo,wn)}}catch{}}}}let ie={};for(let{lane:J}of z.values())ie[J]=(ie[J]||0)+1;return console.log(`[pr-watch] probed ${N.length} link(s) (${Object.entries(ie).map(([J,ge])=>`${J} ${ge}`).join(", ")}) in ${Date.now()-U}ms`),N.length}function tR(e,t){return!e||!t?!1:e.all?!0:String(t.repo||"").toLowerCase()!==e.repo?!1:e.numbers?.length?e.numbers.includes(Number(t.number)):e.event==="push"?!!e.branch&&t.headRef===e.branch||!!t.autoMerge:e.branch?t.headRef===e.branch:!0}var by=!1,lp=!1,up=[],nR=Promise.resolve();async function O5(){for(;lp||up.length;){if(lp){lp=!1;try{await eR()}catch{}continue}let e=up;up=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await eR({...n,only:r=>t.some(o=>tR(o,r)),wantDetail:r=>t.some(o=>o.detail&&tR(o,r))})}catch{}}}function dp(e,t){return e===null?lp=!0:up.push({spec:e,deps:t}),by||(by=!0,nR=O5().finally(()=>{by=!1})),nR}function lR(e,t=void 0){let n=String(e?.repo||"").toLowerCase();if(!/^[^/\s]+\/[^/\s]+$/.test(n))return Promise.resolve();let r=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(o=>Number.isInteger(o)&&o>0);return dp({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var C5=6e4,rR=0;function uR(e=void 0){let t=e?.now??Date.now();return t-rR<C5?Promise.resolve():(rR=t,dp({all:!0},e))}function dR(){dp(null),setInterval(()=>dp(null),oR)}Bo();oh();rd();or();An();var M5=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function N5({tracks:e=me,reconcile:t=nd}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ve(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function pR(e,t={}){let{invalidate:n=Kt,probe:r=lR,poke:o=dT,reconcileOpened:s=N5}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&M5.has(e.action)){if(e.action==="opened"||e.action==="reopened"){a.poked=!0,a.reconciled=!0;try{o()}catch{}s()}a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event}}else(e.event==="check_suite"||e.event==="workflow_run")&&e.action==="completed"?a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event,detail:!0}:e.event==="push"&&!e.deleted&&(a.probe={repo:e.repo,branch:e.headRef||null,event:"push"});return a.probe&&r(a.probe),a}ct();de();or();An();import{randomUUID as P5}from"node:crypto";var D5=2e4,fR=3,Oa=new Map,vy=new Set;async function mR(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 j5(e={}){let{sessions:t=null,tracks:n=null,append:r=C,read:o=K,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Qt(),i=n??await me()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ve(u)||!u.leadBox)continue;let d=(u.boxes||[]).find(f=>f.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let p=c.get(d.name);if(p)if(l.add(d.name),p.alternateOn){let f=(Oa.get(d.name)||0)+1;Oa.set(d.name,f),f===fR&&(vy.add(d.name),await mR(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:P5(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(Oa.get(d.name)||0)>=fR;if(Oa.delete(d.name),f||!vy.has(d.name)){vy.add(d.name);let w=await mR(u,d.name,o);w&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:w,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Oa.keys()])l.has(u)||Oa.delete(u)}var Yc=null,Sy=!1;function hR(){return Yc?()=>{}:(Yc=setInterval(()=>{Sy||(Sy=!0,j5().catch(()=>{}).finally(()=>{Sy=!1}))},D5),Yc.unref?.(),()=>{clearInterval(Yc),Yc=null})}Mo();rd();de();Bo();Fo();Mo();Ki();import{randomUUID as B5}from"node:crypto";async function L5(){let e=await Promise.resolve().then(()=>(or(),f_));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Ty={listRepoPrs:En,readEvents:K,appendEvent:C,sayDeps:L5,currentBranch:Gr};async function F5(e,t=Ty,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Gr)(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 Ay(e,t,{actions:n,notify:r=!1}={},o=Ty){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 F5(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=B5();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 Ey(e,{offerId:t,choice:n}={},r=Ty){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return Jr(`prx:${e.id}:${t}`,()=>U5(e,{offerId:t,choice:n},r))}async function U5(e,{offerId:t,choice:n},r){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=Fr.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,zs(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,qv(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}}Ki();import wr from"node:fs/promises";import Ca from"node:path";import vR from"node:os";import wR from"node:fs/promises";import pp from"node:path";import{execFile as H5}from"node:child_process";import{promisify as W5}from"node:util";import{fileURLToPath as q5}from"node:url";var K5=W5(H5),yR=pp.dirname(q5(import.meta.url)),G5=pp.resolve(yR,"..","..","client","dist"),z5="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",V5=new Map([...z5].map((e,t)=>[e,t]));function J5(e){let t=[],n=0,r=0;for(let o of e){let s=V5.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 Y5(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=J5(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 X5(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 gR=new Map;async function Z5(e){let t;try{t=await wR.stat(e)}catch{return null}let n=gR.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await wR.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:Y5(r.mappings||"")};return gR.set(e,o),o}catch{return null}}var _y=null;function Q5(){return _y||(_y=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):K5("git",["rev-parse","--short","HEAD"],{cwd:pp.resolve(yR,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),_y}var eK=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function tK(e,{distDir:t=G5}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(eK)){let[a,i,c,l]=s,u=await Z5(pp.join(t,"assets",`${i}.map`)),d=u?X5(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let p=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${p}:${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 bR(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await Q5();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await tK(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var nK=()=>process.env.BOSTRAT_STATE_DIR||Ca.join(process.env.HOME||vR.tmpdir(),".bostrat"),mo=()=>Ca.join(nK(),"telemetry"),SR=()=>Ca.join(mo(),".relay-offsets.json"),rK=50,oK=14,sK=3e4,kR=500,Ft=(e,t)=>typeof e=="string"?e.slice(0,t):"";function aK(e,t){if(!e||typeof e!="object")return null;let n=Ft(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:Ft(e.anonId,64),sessionId:Ft(e.sessionId,64),userId:Ft(e.userId,64),release:Ft(e.release,64),appVersion:Ft(e.appVersion,32),route:Ft(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:Ft(e.error.kind,256),message:Ft(e.error.message,8192),stack:Ft(e.error.stack,32768),fingerprint:Ft(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:Ft(s?.n,64),r:Ft(s?.r,128)}))),o}var TR=e=>new Date(e).toISOString().slice(0,10),xR=0;async function AR(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,rK):[]).map(s=>aK(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await bR(s);await wr.mkdir(mo(),{recursive:!0});let o=Ca.join(mo(),`${TR(t)}.ndjson`);return await wr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
755
|
+
`).join(""),"utf8"),t-xR>6*3600*1e3&&(xR=t,iK(t).catch(()=>{})),r.length}async function iK(e=Date.now()){let t=TR(e-oK*24*3600*1e3),n=[];try{n=await wr.readdir(mo())}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 wr.rm(Ca.join(mo(),s),{force:!0});let o=await _R();for(let s of r)delete o[s];await IR(o)}var fp=new Map;function ER(e,t=Date.now()){let n=e||"unknown",r=fp.get(n);return r||(fp.size>512&&fp.clear(),r={tokens:40,at:t},fp.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 _R(){try{return JSON.parse(await wr.readFile(SR(),"utf8"))||{}}catch{return{}}}async function IR(e){await wr.mkdir(mo(),{recursive:!0}),await wr.writeFile(SR(),JSON.stringify(e),"utf8")}function cK(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||vR.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 lK({fetchImpl:e=fetch}={}){let t=await J_();if(!t)return{sent:0,disabled:!0};let n;try{n=(await wr.readdir(mo())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await _R(),o=0;for(let s of n){let a=Ca.join(mo(),s),i=r[s]||0,c;try{let p=await wr.open(a,"r");try{let f=(await p.stat()).size;if(f<=i)continue;c=Buffer.alloc(f-i),await p.read(c,0,c.length,i)}finally{await p.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
756
|
+
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=kR){let f=d.slice(p,p+kR).map(m=>cK(m)),w=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!w||!w.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await IR(r)}return{sent:o,disabled:!1}}var Iy=null,$y=!1;function $R(){Iy||(Iy=setInterval(async()=>{if(!$y){$y=!0;try{await lK()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{$y=!1}}},sK),Iy.unref?.())}import hp from"node:fs/promises";import NR from"node:path";import uK from"node:os";var PR=()=>process.env.BOSTRAT_STATE_DIR||NR.join(process.env.HOME||uK.tmpdir(),".bostrat"),DR=()=>NR.join(PR(),"dictation-glossary.json"),dK=100,pK=500,fK=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],wp=null,Ry=null,Oy=Promise.resolve();function RR(){let e={},t=Date.now();for(let n of fK)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function jR(){return wp||(Ry??=hp.readFile(DR(),"utf8").then(e=>JSON.parse(e)?.terms??RR()).catch(()=>RR()).then(e=>wp=e).finally(()=>Ry=null),Ry)}async function OR(){let e=JSON.stringify({terms:wp},null,2),t=DR(),n=`${t}.${process.pid}.tmp`;await hp.mkdir(PR(),{recursive:!0}),await hp.writeFile(n,e),await hp.rename(n,t)}function mK(){return Oy=Oy.then(OR,OR),Oy.catch(()=>{})}async function Cy(){return Object.values(await jR()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var hK=e=>Math.max(1,Math.ceil(e.length/4));async function wK(){let e=[],t=0;for(let{term:n}of await Cy()){let r=hK(n);if(!(t+r>pK)&&(e.push(n),t+=r,e.length>=dK))break}return e}async function gK(e,t){let n=await jR(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var CR=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),mp=e=>e.toLowerCase();function MR(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function BR(e,t){let n=CR(e),r=CR(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]=mp(n[u])===mp(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;)mp(n[c])===mp(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(MR(r[l])&&i.add(r[l]),l++);for(;l<s;l++)MR(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await gK(u,"learned");return await mK(),[...i]}var Xc=null;function yK(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 LR(){if(!Xc)return null;let e=await Cc();if(!e||e.apiKey.slice(-6)!==Xc.keyTail)return null;let{keyTail:t,...n}=Xc;return n}async function FR(e,t){let n=await Cc();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 wK())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=yK(o.status,i);c.persistent&&(Xc={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 Xc=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}de();var My={"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 bK(e){if(!e)return null;let t=String(e).trim();if(My[t])return t;let n=null;for(let r of Object.keys(My))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function kK(e,t={}){let n=My[bK(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 xK(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 Zc=null;async function vK(){if(!Zc)return null;let e=await Ud();if(!e||e.apiKey.slice(-6)!==Zc.keyTail)return null;let{keyTail:t,...n}=Zc;return n}async function SK(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=xK(t.status,o);s.persistent&&(Zc={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}Zc=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 TK={openai:SK};async function UR(){let e=await Ud();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await vK()}:{configured:!1}}async function HR(){let e=await Ud();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 TK[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function WR(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=kK(t,s),i=await C(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}}An();var XR=Te.dirname(EK(import.meta.url)),qR=Number(process.env.PORT)||3362,yp=wg(process.env),ZR=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Ma=o$(process.env.BOSTRAT_ALLOW_IPS),By=s$(process.env.BOSTRAT_VIEW_FRONT_CIDRS,rp),Ly=i$(),QR=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",S=Ut();S.disable("x-powered-by");S.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),np(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});S.use((e,t,n)=>{if(Gc(e)||Ma.allowed(Ma.clientIp(e))||By.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(f$);S.use(u$({token:Ly,enforce:QR}));S.use("/api/mocks",Ut.json({limit:"1mb"}));S.use("/api/mocks",Ut.raw({type:"image/png",limit:"24mb"}));S.use("/api/mocks",Ut.raw({type:["video/mp4","video/webm"],limit:"105mb"}));S.use("/api/runbooks",Ut.json({limit:"1mb"}));S.use("/api/tracks/adopt",Ut.json({limit:"1mb"}));S.use("/api/telemetry",Ut.json({limit:"256kb"}));S.use("/api/hook",Ut.json({limit:"2mb"}));S.use("/api/dictation",Ut.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",Ut.raw({type:()=>!0,limit:"25mb"}));S.use(Ut.json({limit:"64kb"}));S.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:Rt,hostTmuxAlive:await Er(),autoTriggerPct:ZR,plane:Na?Na.status():null})});S.get("/api/sessions",async(e,t)=>{t.json({sessions:await Qt()})});S.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await _r(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});S.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});if(!await ke(n))return t.status(404).json({error:"session not found"});try{await en(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});S.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});let r=await ke(n);if(!r)return t.status(404).json({error:"session not found"});let o=await ci(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});S.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});try{let r=await rx(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});S.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=nu(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 KR=10;async function eO(){let e=await me(),t=await k_(e);return{tracks:e,views:await Bh(),queuedOf:n=>Vo(n),jobsOf:n=>t.get(n)??[]}}S.get("/api/idle-guard",async(e,t)=>{try{t.json(pg(await eO()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Xt=Qo(),CK=P_({kind:Xt}),Qc=null,vt=()=>Qc&&["auto","prompt","off"].includes(Qc)?Qc:CK,jy=null,MK=Rc.has(Xt),tO=async({to:e,from:t})=>!MK||Nd(await Oc())?!1:(tl.arm({to:e,from:t,kind:Xt}),!0);async function NK(e){let t=vt();Qc=e??null;let n=vt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){tl.cancel();return}jy&&await tO(jy)}}var nO=(()=>{try{return OK(import.meta.url)("../package.json").version||null}catch{return null}})(),tl=j_({isBusy:async()=>pg(await eO()).busy,onStuck:({to:e,from:t})=>Bn({current:t,latest:e,installKind:Xt,policy:vt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Bn({current:t,latest:e,installKind:Xt,policy:vt(),status:"updating",reason:"auto"}):Bn({current:t,latest:e,installKind:Xt,policy:vt(),status:"failed",error:n?.reason})).catch(()=>{})});S.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Xt,policy:vt(),policySource:Qc?"plane":"node",current:nO,status:await Oc(),armed:tl.armed})});S.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Pd({to:n,kind:Xt,reason:"manual"});if(r.ok)return tl.cancel(),Bn({current:r.from,latest:n,installKind:Xt,policy:vt(),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 rO(){try{let e=await D_();e&&await Bn({current:e.from,latest:e.to,installKind:e.kind||Xt,policy:vt(),status:e.status,error:e.error,reason:e.reason}),await Rw({current:nO,installKind:Xt,policy:vt()})}catch{}}rO();setInterval(rO,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await me()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>KR){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,KR).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:bc,agentAvailability:await gd(),tickets:await lI()})});S.get("/api/cost",async(e,t)=>{try{let n=await me();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Xg(n);if(r.cursorUsage=vh(),e.query.format==="text")return t.type("text/plain").send(NI(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/roundup",async(e,t)=>{try{let n=await Qg(await me(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(LI(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/spend",async(e,t)=>{try{let n=await qI(await me(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=vh(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Ri(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(Ri())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await mu(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});S.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await QI(await me(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Qy(e.get(Hn),{byToken:xc,legacy:()=>Vi(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Fa("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Fa("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&&Fa("mismatch",{track:r.track.id,box:r.box.name}),Fa(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 OI(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&kl(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await xc(e.get(Hn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await YS(n.track,n.box)).join(`
|
|
753
757
|
`)+`
|
|
754
|
-
`)}catch{t.status(204).end()}});S.post("/api/git/committed",async(e,t)=>{try{let n=await pc(e.get(Bn));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?Ae.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Ae.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Ae.sep))?s:o;IS(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await RS(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});S.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await OS(n,String(e.body?.sha||""))})}catch(n){let r=n?.message||String(n);t.status(/no standing answer/i.test(r)?409:400).json({error:r})}});S.get("/api/usage/rework",async(e,t)=>{try{t.json(await oI(await pe(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/telemetry",async(e,t)=>{try{if(!L$(_a.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await B$(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/repos",async(e,t)=>{try{let n=await Ln(),r=new Set((await Promise.all(n.map(a=>_e(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await La()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/github/token",async(e,t)=>{try{t.json(await nl(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/github/status",(e,t)=>{t.json({configured:tl(),calls:Vv(),latency:Xv(),webhook:l$()})});S.post("/api/github/event",Ut.json({limit:"64kb"}),(e,t)=>{let n=i$(e.body,{onAccept:r=>A$(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});S.post("/api/github/refresh",(e,t)=>{Gy(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await mI(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await hI(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function vy(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 Gc({repo:e=null,number:t=null,branch:n=null}){let r=e?await _e(e):null;if(r&&t!=null){let s=await st(r,t),a=s&&await oe(s.trackId);if(a)return a}if(!n)return null;let o=await pe();return Au(o,n,s=>vy(s,e,r))}async function wR({repo:e=null,number:t=null,branch:n=null}){let r=await Gc({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ve(r))return r;if(!n)return null;let o=e?await _e(e).catch(()=>null):null,s=await pe();return Au(s,n,a=>!Ve(a)&&vy(a,e,o))}async function mn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Gc({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await _e(s).catch(()=>null):null);await C({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 k5(e){let t=(await pe()).filter(n=>!Ve(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await _e(n.path);if(r)for(let o of n.prs){if(await st(r,o.number))continue;let s=await Au(t,o.headRefName,a=>vy(a,n.path,r));s&&await Ct({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}S.get("/api/git/prs",async(e,t)=>{try{let n=await oS();await k5(n.repos).catch(()=>{}),await Jd(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{},p=null,f=null,h=null;try{if(s){let x=o||"merge";if(!Ri.has(x))throw new Error(`invalid merge method: ${x}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let I=await _e((await Xn(n)).path);if(!I)throw new Error("repo has no GitHub remote");let R=await Gc({repo:n,number:y,branch:l}).catch(()=>null);if(!R)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Oi(n,y),await Ct({repo:I,number:y,trackId:R.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Ei(I,y,{merge:{method:x}}))throw new Error("couldn't record the auto-merge arm");return await Io(I,y,i===!0),await $o(I,y,c===!0&&i!==!0),Gt(),await mn("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:R,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}p=await Gc({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await Jh(p,"merge",{count:1,summary:`merging PR #${r}`}));let w=await zs(n,r,{method:o||"squash"});await mn("merged",{number:w.number,url:d,title:u,branch:l,repo:n});let g=await _e(n).catch(()=>null),k=!1;if(g&&(i===!0&&await Io(g,w.number,!0).catch(()=>{}),c===!0&&i!==!0&&await $o(g,w.number,!0).catch(()=>{}),await Qn(g,w.number).catch(()=>{}),await Ko(g,w.number).catch(()=>{}),k=await Zn(g,w.number).catch(()=>!1),await Dr(g,w.number,"merged").catch(()=>{})),i===!0&&!k){let x=await Gc({repo:n,number:w.number,branch:l}).catch(()=>null);x&&!x.archivedAt&&await Go(x.id).catch(()=>{})}t.json(w)}catch(w){h=w;let g=w?.message||String(w),k=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(g)?400:502;t.status(k).json({error:g})}finally{p&&f&&await Xh(p,f,h?"failed":"completed",h).catch(()=>{})}});S.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await _e((await Xn(n)).path).catch(()=>null),l=c?await _i(c,i).catch(()=>null):null;try{await Cm(n,i)}catch(u){if(!l)throw u}Gt(),await mn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await _e(n).catch(()=>null),s=await lS(n,{method:r||"merge",onMerged:a=>{mn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Qn(o,a.number).catch(()=>{}).then(()=>Ko(o,a.number)).catch(()=>{}).then(()=>Zn(o,a.number)).catch(()=>{}).then(()=>Dr(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});S.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:cS()})});S.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{},i=null,c=null,l=null;try{let u=a||"merge";if(!Ri.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await oe(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await Vn()).filter(w=>w.trackId===d.id&&w.state!=="merged"&&w.state!=="closed").sort(Ci);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await Jh(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let w of p)await Io(w.repo,w.number,o===!0).catch(()=>{}),await $o(w.repo,w.number,s===!0&&o!==!0).catch(()=>{});let f=[],h=[];for(let w of p){let g={repo:w.repo,number:w.number,ok:!0,action:"armed"};f.push(g);try{let k=await Oo(w.repo);if(!k)throw new Error(`no local checkout for ${w.repo}`);let x=await Vs(k,w.number);h.push({r:g,l:w,repoPath:k,pr:x,plan:sS(x,{update:r!==!1})})}catch(k){g.ok=!1,g.action="failed",g.reason=k?.message||String(k)}}for(let{r:w,l:g,repoPath:k,pr:x,plan:y}of aS(h))try{if(y.action==="skip")w.action="skipped",w.reason=y.reason;else if(y.action==="merge")await zs(k,g.number,{method:u}),await mn("merged",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:k,track:d}),await Qn(g.repo,g.number).catch(()=>{}),await Ko(g.repo,g.number).catch(()=>{}),await Zn(g.repo,g.number).catch(()=>{}),await Dr(g.repo,g.number,"merged").catch(()=>{}),w.action="merged";else{if(y.action==="update-arm"&&(await Oi(k,g.number).catch(()=>{}),w.action="updated-armed"),!await Ei(g.repo,g.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await mn("auto-armed",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:k,track:d,...w.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(I){w.ok=!1,w.action="failed",w.reason=I?.message||String(I)}if(Gt(),!f.some(w=>w.ok)){if(o===!0)for(let w of p)await Io(w.repo,w.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let w of p)await $o(w.repo,w.number,!1).catch(()=>{});return t.status(502).json({error:f.map(w=>`${w.repo}#${w.number}: ${w.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await Xh(i,c,l?"failed":"completed",l).catch(()=>{})}});S.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await oe(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Vn()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await _i(a.repo,a.number).catch(()=>null),c=await Oo(a.repo).catch(()=>null);c&&await Cm(c,a.number).catch(()=>{}),i&&(await mn("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}))}Gt(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await oe(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=jt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(w=>{let g=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!g)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return g});else if(d=await Do(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let w of d)try{let g=await Nm(w.dir,{title:r,body:o,base:s}),k=w.repo||await _e(w.dir).catch(()=>null);k&&g.number&&await Ct({repo:k,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await mn("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??w.name}}),f.push({repo:w.name,slug:k??null,ok:!0,url:g.url,number:g.number})}catch(g){f.push({repo:w.name,slug:w.repo??null,ok:!1,error:g?.message||String(g)})}let h=f.filter(w=>w.ok);return h.length?t.json({ok:!0,multi:!0,results:f,url:h[0].url,number:h[0].number}):t.status(502).json({error:f.map(w=>`${w.repo}: ${w.error}`).join("; "),results:f})}let c=await Nm(i.cwd,{title:r,body:o,base:s}),l=await _e(i.cwd).catch(()=>null);l&&c.number&&await Ct({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await mn("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});S.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Do(n),o=new Map(r.map(a=>[a.base,a])),s=jt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await _w(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Iw(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});S.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Vs(n,r),s=await _e(n).catch(()=>null),a=await wR({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=jt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await uS(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Ae.join("ci",i.name):"ci",d=Ae.join(a.cwd,".bostrat",u),p=Ae.join(".bostrat",u,`pr-${o.number}-fail.log`);await ht.promises.mkdir(d,{recursive:!0}),await ht.promises.writeFile(Ae.join(a.cwd,p),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
755
|
-
${
|
|
758
|
+
`)}catch{t.status(204).end()}});S.post("/api/git/committed",async(e,t)=>{try{let n=await xc(e.get(Hn));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?Te.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Te.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Te.sep))?s:o;XS(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await QS(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});S.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await eT(n,String(e.body?.sha||""))})}catch(n){let r=n?.message||String(n);t.status(/no standing answer/i.test(r)?409:400).json({error:r})}});S.get("/api/usage/rework",async(e,t)=>{try{t.json(await KI(await me(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/telemetry",async(e,t)=>{try{if(!ER(Ma.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await AR(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/repos",async(e,t)=>{try{let n=await Wn(),r=new Set((await Promise.all(n.map(a=>_e(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await qa()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/github/token",async(e,t)=>{try{t.json(await dl(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/github/status",(e,t)=>{t.json({configured:ul(),calls:wS(),latency:kS(),webhook:Y$()})});S.post("/api/github/event",Ut.json({limit:"64kb"}),(e,t)=>{let n=V$(e.body,{onAccept:r=>pR(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});S.post("/api/github/refresh",(e,t)=>{lb(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await t$(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await n$(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Fy(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 el({repo:e=null,number:t=null,branch:n=null}){let r=e?await _e(e):null;if(r&&t!=null){let s=await at(r,t),a=s&&await oe(s.trackId);if(a)return a}if(!n)return null;let o=await me();return Pu(o,n,s=>Fy(s,e,r))}async function oO({repo:e=null,number:t=null,branch:n=null}){let r=await el({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ve(r))return r;if(!n)return null;let o=e?await _e(e).catch(()=>null):null,s=await me();return Pu(s,n,a=>!Ve(a)&&Fy(a,e,o))}async function hn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await el({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await _e(s).catch(()=>null):null);await C({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 PK(e){let t=(await me()).filter(n=>!Ve(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await _e(n.path);if(r)for(let o of n.prs){if(await at(r,o.number))continue;let s=await Pu(t,o.headRefName,a=>Fy(a,n.path,r));s&&await Ct({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}S.get("/api/git/prs",async(e,t)=>{try{let n=await _S();await PK(n.repos).catch(()=>{}),await ap(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{},p=null,f=null,w=null;try{if(s){let x=o||"merge";if(!Di.has(x))throw new Error(`invalid merge method: ${x}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let $=await _e((await tr(n)).path);if(!$)throw new Error("repo has no GitHub remote");let O=await el({repo:n,number:y,branch:l}).catch(()=>null);if(!O)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ji(n,y),await Ct({repo:$,number:y,trackId:O.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Ci($,y,{merge:{method:x}}))throw new Error("couldn't record the auto-merge arm");return await No($,y,i===!0),await Po($,y,c===!0&&i!==!0),Kt(),await hn("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:O,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}p=await el({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await uw(p,"merge",{count:1,summary:`merging PR #${r}`}));let m=await Zs(n,r,{method:o||"squash"});await hn("merged",{number:m.number,url:d,title:u,branch:l,repo:n});let g=await _e(n).catch(()=>null),b=!1;if(g&&(i===!0&&await No(g,m.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Po(g,m.number,!0).catch(()=>{}),await rr(g,m.number).catch(()=>{}),await Xo(g,m.number).catch(()=>{}),b=await nr(g,m.number).catch(()=>!1),await Hr(g,m.number,"merged").catch(()=>{})),i===!0&&!b){let x=await el({repo:n,number:m.number,branch:l}).catch(()=>null);x&&!x.archivedAt&&await Yo(x.id).catch(()=>{})}t.json(m)}catch(m){w=m;let g=m?.message||String(m),b=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(g)?400:502;t.status(b).json({error:g})}finally{p&&f&&await dw(p,f,w?"failed":"completed",w).catch(()=>{})}});S.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await _e((await tr(n)).path).catch(()=>null),l=c?await Mi(c,i).catch(()=>null):null;try{await Hm(n,i)}catch(u){if(!l)throw u}Kt(),await hn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await _e(n).catch(()=>null),s=await CS(n,{method:r||"merge",onMerged:a=>{hn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&rr(o,a.number).catch(()=>{}).then(()=>Xo(o,a.number)).catch(()=>{}).then(()=>nr(o,a.number)).catch(()=>{}).then(()=>Hr(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});S.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:OS()})});S.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{},i=null,c=null,l=null;try{let u=a||"merge";if(!Di.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await oe(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await Xn()).filter(m=>m.trackId===d.id&&m.state!=="merged"&&m.state!=="closed").sort(Bi);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await uw(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let m of p)await No(m.repo,m.number,o===!0).catch(()=>{}),await Po(m.repo,m.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let m of p){let g={repo:m.repo,number:m.number,ok:!0,action:"armed"};f.push(g);try{let b=await jo(m.repo);if(!b)throw new Error(`no local checkout for ${m.repo}`);let x=await Xs(b,m.number);w.push({r:g,l:m,repoPath:b,pr:x,plan:IS(x,{update:r!==!1})})}catch(b){g.ok=!1,g.action="failed",g.reason=b?.message||String(b)}}for(let{r:m,l:g,repoPath:b,pr:x,plan:y}of $S(w))try{if(y.action==="skip")m.action="skipped",m.reason=y.reason;else if(y.action==="merge")await Zs(b,g.number,{method:u}),await hn("merged",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:b,track:d}),await rr(g.repo,g.number).catch(()=>{}),await Xo(g.repo,g.number).catch(()=>{}),await nr(g.repo,g.number).catch(()=>{}),await Hr(g.repo,g.number,"merged").catch(()=>{}),m.action="merged";else{if(y.action==="update-arm"&&(await ji(b,g.number).catch(()=>{}),m.action="updated-armed"),!await Ci(g.repo,g.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await hn("auto-armed",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:b,track:d,...m.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch($){m.ok=!1,m.action="failed",m.reason=$?.message||String($)}if(Kt(),!f.some(m=>m.ok)){if(o===!0)for(let m of p)await No(m.repo,m.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let m of p)await Po(m.repo,m.number,!1).catch(()=>{});return t.status(502).json({error:f.map(m=>`${m.repo}#${m.number}: ${m.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await dw(i,c,l?"failed":"completed",l).catch(()=>{})}});S.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await oe(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Xn()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Mi(a.repo,a.number).catch(()=>null),c=await jo(a.repo).catch(()=>null);c&&await Hm(c,a.number).catch(()=>{}),i&&(await hn("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}))}Kt(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await oe(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=jt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(m=>{let g=u.find(b=>b.name===m||b.repo===m||b.base===m||b.dir===m);if(!g)throw new Error(`invalid repo "${m}" \u2014 not tagged on this Track`);return g});else if(d=await Ho(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let m of d)try{let g=await qm(m.dir,{title:r,body:o,base:s}),b=m.repo||await _e(m.dir).catch(()=>null);b&&g.number&&await Ct({repo:b,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await hn("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??m.name}}),f.push({repo:m.name,slug:b??null,ok:!0,url:g.url,number:g.number})}catch(g){f.push({repo:m.name,slug:m.repo??null,ok:!1,error:g?.message||String(g)})}let w=f.filter(m=>m.ok);return w.length?t.json({ok:!0,multi:!0,results:f,url:w[0].url,number:w[0].number}):t.status(502).json({error:f.map(m=>`${m.repo}: ${m.error}`).join("; "),results:f})}let c=await qm(i.cwd,{title:r,body:o,base:s}),l=await _e(i.cwd).catch(()=>null);l&&c.number&&await Ct({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await hn("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});S.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ho(n),o=new Map(r.map(a=>[a.base,a])),s=jt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Hw(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Ww(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});S.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Xs(n,r),s=await _e(n).catch(()=>null),a=await oO({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=jt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await MS(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Te.join("ci",i.name):"ci",d=Te.join(a.cwd,".bostrat",u),p=Te.join(".bostrat",u,`pr-${o.number}-fail.log`);await wt.promises.mkdir(d,{recursive:!0}),await wt.promises.writeFile(Te.join(a.cwd,p),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
759
|
+
${b.log??"(no fetchable log \u2014 see link)"}
|
|
756
760
|
`).join(`
|
|
757
|
-
`));let f=l.map(k=>k.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await Dt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. Failing check${l.length===1?"":"s"}: ${f}. The failure log is at ${p} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Ct({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await $v(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await mn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.name),log:p})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Vs(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await _e(n).catch(()=>null),a=await wR({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=jt(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 Dt(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 Ct({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Rv(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await mn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=Nr.filter(a=>e.body.actions.includes(a)),!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;await Wr(`pr:${e.params.id}:${n}`,async()=>{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await G(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 iy(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 Dt(a.id,Ws(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 C({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&&vw(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:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await cy(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:II(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await qu({trackId:e.query.track||void 0});t.json({views:n.map(Vc(e)),maxLive:Gr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await jT(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Vc(e)(n),session:ea(n.slug)})});S.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await oe(n):null;if(!i&&s&&(i=(await pe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ae.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Eh({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})}});S.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Vc(e)(await BT(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Vc(e)(await LT(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/views/:id",async(e,t)=>{try{t.json(await FT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var x5=[["linear",Yo,ug],["jira",Rd,cg]],wy=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await ig(),r={};for(let[o,s,a]of x5){let i=await s();if(!i){wy.delete(o),r[o]={configured:!1};continue}let c=wy.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},wy.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await ig()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await i_(o),i=n?await oe(n):null;!i&&r&&(i=(await pe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ae.sep)))||null),i&&await C({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 os=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await pe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ae.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await tg()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});S.get("/api/roadmap",async(e,t)=>{try{t.json(await ng())}catch(n){t.status(502).json({error:n?.message||String(n)})}});S.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await os(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 wa(o);await Ed(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});S.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await os(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Ed(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var gR=async(e,t,n)=>{if(e)return{ref:e,track:await os(t,n)};let r=await os(t,n),o=r?await og(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};S.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await gR(o,n,r),l=await QE({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});S.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await gR(e.query.circuit,e.query.track,e.query.cwd);t.json(await rg(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});S.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await os(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await n_(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});S.get("/api/decisions",async(e,t)=>{try{let n=await os(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await r_(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});S.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await os(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await og(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 e_(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});S.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await pe()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Ae.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await ba(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await oe(r):null;if(!o&&n&&(o=(await pe()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Ae.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await S_(),ticket:await Dn(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});S.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Dn(e.params.id)})});S.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ba(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});S.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await vg(e.params.id)})});var lp=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;S.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await __(e.params.id))}catch(n){let r=n?.message||String(n);t.status(lp(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Sg(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(lp(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await I_(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(lp(r)).json({error:r})}});S.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await $_(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(lp(s)).json({error:s})}});S.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Bw(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});S.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await pe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ae.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await O_(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await R_()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});S.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await C_(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});S.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Ji({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/mocks/:id",async(e,t)=>{let n=await or(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var zc={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await or(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=cn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=zi(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,zc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await or(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=cn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=zi(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,zc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await or(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=cn(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(Vt(n,r.v).html,zc,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});S.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Ju(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let d=a?await oe(a):null;if(!d&&c&&(d=(await pe()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+Ae.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(h=>h.name===d.leadBox),f=await nA({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:p?.agent||null});t.json({ok:!0,mock:f})}catch(d){let p=d?.message||String(d);t.status(/required|invalid|must be/i.test(p)?400:500).json({error:p})}});S.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Ju(n)?n:null;try{let o=await rA(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});S.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await aA(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Dt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await TA(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await iA(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/flows",async(e,t)=>{try{t.json({flows:await bA({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/flows/:id",async(e,t)=>{let n=await kA(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});S.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await pe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ae.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await xA({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});S.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await vA(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/flows/:id",async(e,t)=>{try{t.json(await SA(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await pe(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Ae.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await CA({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/runbooks/:id",async(e,t)=>{let n=await Lh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});S.get("/api/runbooks/:id/md",async(e,t)=>{let n=await Lh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=RA(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 MA(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(cr(n,r.v),zc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await ji())});S.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await pe()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Ae.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await ji();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 Qm({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});S.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await pe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ae.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await PI(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var $a=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await pe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ae.sep)))||null),n};S.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await $a(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,result:await sT(s,o,{ship:{changedRepos:Do}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});S.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await $a(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await iT(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});S.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await $a(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:sh,...await pT(r)})});var sR=new Set(["reconcile","fixing-ci","rebase","merge"]);S.get("/api/status",async(e,t)=>{let n=await $a(null,typeof e.query?.cwd=="string"?e.query.cwd:null).catch(()=>null);if(!n)return t.status(400).json({error:"a cwd inside a Track workspace is required"});t.json({ok:!0,trackId:n.id,status:n.status})});S.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await $a(n,r);if(!c)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let l=o==="fix-ci"?"fixing-ci":o;if(l!=null&&!sR.has(l))throw new Error(`phase must be one of ${[...sR].join(" | ")}`);let u=s==null?null:String(s).trim();if(u&&u.length>120)throw new Error("summary is too long (cap is 120 characters)");let d=m5();await C(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await lT(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Dt,unarchiveTrack:md})})}catch(a){let i=a?.message||String(a),c=a?.code==="removed"?410:/unknown offer/i.test(i)?404:/already answered/i.test(i)?409:/required|not on this card/i.test(i)?400:/unreachable/i.test(i)?503:502;t.status(c).json({error:i})}});S.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await aT(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});S.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await cT(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});rt.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||oe(e.trackId).then(async s=>{if(s&&(n&&await Fi(s,n,o),t==="opened"||t==="pushed")){let a=dT(await G(s).catch(()=>[]));a&&await Fi(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var yR=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await oe(n):null;if(!s&&r&&(s=(await pe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ae.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await ji();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=BI(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};S.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await yR(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await LI(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});S.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await yR(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await FI(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});S.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await $a(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await ng()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?zg:Bc;t.json({ok:!0,trackId:a.id,proposal:await XI(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var Sy=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;S.post("/api/tracks/:id/planning-offer",async(e,t)=>{let{proposalId:n,action:r,results: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 ZI(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(Sy(a)).json({error:a})}});S.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await HI(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(Sy(a)).json({error:a})}});S.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await WI(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(Sy(s)).json({error:s})}});S.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await DI(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});S.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await pe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ae.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await NA({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});S.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await PA(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});S.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await DA(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await jA(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/changelog/draft",async(e,t)=>{try{let n=r$(e.query.since||null);t.json({since:n,repos:Jg(),...await o$(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var bR=async({trackId:e,cwd:t})=>{let n=e?await oe(e):null;return!n&&t&&(n=(await pe()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Ae.sep)))||null),n};S.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await bR({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 s$(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});S.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await bR({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 a$(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var v5=120,gy=400,Ia=null,aR=null;S.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>gy)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${gy} cap; a notification is a nudge, put the detail in chat`});let c=Xi(`${n||""}
|
|
758
|
-
${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
|
|
761
|
+
`));let f=l.map(b=>b.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",m=await Dt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${w}. Failing check${l.length===1?"":"s"}: ${f}. The failure log is at ${p} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Ct({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Zv(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(b=>b.name)}}),t.json({ok:!0,leadBox:m.leadBox,queued:!!m.queued,checks:l.map(b=>b.name),log:p})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Xs(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await _e(n).catch(()=>null),a=await oO({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=jt(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 Dt(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 Ct({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Qv(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=Fr.filter(a=>e.body.actions.includes(a)),!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;await Jr(`pr:${e.params.id}:${n}`,async()=>{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await K(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 Ay(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 Dt(a.id,zs(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 C({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&&jw(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:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Ey(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 nl=e=>t=>({...t,url:h$(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await Bh({trackId:e.query.track||void 0});t.json({views:n.map(nl(e)),maxLive:Xr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await mA(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:nl(e)(n),session:sa(n.slug)})});S.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await oe(n):null;if(!i&&s&&(i=(await me()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Te.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Fh({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:nl(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});S.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:nl(e)(await hA(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:nl(e)(await wA(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/views/:id",async(e,t)=>{try{t.json(await gA(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var DK=[["linear",es,Ig],["jira",Fd,Eg]],Ny=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Ag(),r={};for(let[o,s,a]of DK){let i=await s();if(!i){Ny.delete(o),r[o]={configured:!1};continue}let c=Ny.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},Ny.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await Ag()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await V_(o),i=n?await oe(n):null;!i&&r&&(i=(await me()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Te.sep)))||null),i&&await C({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 ls=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await me()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Te.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await bg()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});S.get("/api/roadmap",async(e,t)=>{try{t.json(await kg())}catch(n){t.status(502).json({error:n?.message||String(n)})}});S.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await ls(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 va(o);await Dd(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});S.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await ls(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Dd(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var sO=async(e,t,n)=>{if(e)return{ref:e,track:await ls(t,n)};let r=await ls(t,n),o=r?await vg(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};S.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await sO(o,n,r),l=await F_({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});S.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await sO(e.query.circuit,e.query.track,e.query.cwd);t.json(await xg(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});S.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await ls(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await W_(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});S.get("/api/decisions",async(e,t)=>{try{let n=await ls(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await q_(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});S.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await ls(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await vg(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 U_(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});S.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await me()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Te.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await Aa(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await oe(r):null;if(!o&&n&&(o=(await me()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Te.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await uI(),ticket:await Fn(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});S.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Fn(e.params.id)})});S.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Aa(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});S.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Fg(e.params.id)})});var bp=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;S.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await mI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(bp(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Ug(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(bp(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await hI(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(bp(r)).json({error:r})}});S.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await wI(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(bp(s)).json({error:s})}});S.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Qw(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});S.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await me()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Te.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await yI(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await gI()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});S.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await bI(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});S.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await ac({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/mocks/:id",async(e,t)=>{let n=await cr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var rl={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await cr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=an(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=oc(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,rl,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await cr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=an(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=oc(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,rl,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await cr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=an(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(zt(n,r.v).html,rl,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});S.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&sd(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 me()).find(m=>m.cwd&&(c===m.cwd||c.startsWith(m.cwd+Te.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(w=>w.name===d.leadBox),f=await MA({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:p?.agent||null});t.json({ok:!0,mock:f})}catch(d){let p=d?.message||String(d);t.status(/required|invalid|must be/i.test(p)?400:500).json({error:p})}});S.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&sd(n)?n:null;try{let o=await NA(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});S.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await jA(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Dt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await tE(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await BA(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/flows",async(e,t)=>{try{t.json({flows:await YA({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/flows/:id",async(e,t)=>{let n=await XA(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});S.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await me()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Te.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await ZA({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});S.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await QA(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/flows/:id",async(e,t)=>{try{t.json(await eE(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await me(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Te.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await lE({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/runbooks/:id",async(e,t)=>{let n=await Qh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});S.get("/api/runbooks/:id/md",async(e,t)=>{let n=await Qh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=iE(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 uE(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(pr(n,r.v),rl,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await Wi())});S.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await me()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Te.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Wi();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 lh({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});S.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await me()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Te.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await v$(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var Pa=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await me()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Te.sep)))||null),n};S.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Pa(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,result:await IT(s,o,{ship:{changedRepos:Ho}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});S.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Pa(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await RT(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});S.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await Pa(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:hh,...await PT(r)})});var GR=new Set(["reconcile","fixing-ci","rebase","merge"]);S.get("/api/status",async(e,t)=>{let n=await Pa(null,typeof e.query?.cwd=="string"?e.query.cwd:null).catch(()=>null);if(!n)return t.status(400).json({error:"a cwd inside a Track workspace is required"});t.json({ok:!0,trackId:n.id,status:n.status})});S.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await Pa(n,r);if(!c)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let l=o==="fix-ci"?"fixing-ci":o;if(l!=null&&!GR.has(l))throw new Error(`phase must be one of ${[...GR].join(" | ")}`);let u=s==null?null:String(s).trim();if(u&&u.length>120)throw new Error("summary is too long (cap is 120 characters)");let d=$K();await C(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await CT(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Dt,unarchiveTrack:Td})})}catch(a){let i=a?.message||String(a),c=a?.code==="removed"?410:/unknown offer/i.test(i)?404:/already answered/i.test(i)?409:/required|not on this card/i.test(i)?400:/unreachable/i.test(i)?503:502;t.status(c).json({error:i})}});S.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await $T(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});S.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await OT(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});ot.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||oe(e.trackId).then(async s=>{if(s&&(n&&await Gi(s,n,o),t==="opened"||t==="pushed")){let a=NT(await K(s).catch(()=>[]));a&&await Gi(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var aO=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await oe(n):null;if(!s&&r&&(s=(await me()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Te.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Wi();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=A$(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};S.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await aO(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await E$(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});S.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await aO(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await _$(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});S.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await Pa(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await kg()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?py:zc;t.json({ok:!0,trackId:a.id,proposal:await B$(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var Uy=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;S.post("/api/tracks/:id/planning-offer",async(e,t)=>{let{proposalId:n,action:r,results: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 L$(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(Uy(a)).json({error:a})}});S.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await $$(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(Uy(a)).json({error:a})}});S.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await R$(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(Uy(s)).json({error:s})}});S.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await S$(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});S.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await me()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Te.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await dE({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});S.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await pE(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});S.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await fE(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await mE(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/changelog/draft",async(e,t)=>{try{let n=q$(e.query.since||null);t.json({since:n,repos:my(),...await K$(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var iO=async({trackId:e,cwd:t})=>{let n=e?await oe(e):null;return!n&&t&&(n=(await me()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Te.sep)))||null),n};S.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await iO({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 G$(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});S.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await iO({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 z$(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var jK=120,Py=400,Na=null,zR=null;S.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>Py)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Py} cap; a notification is a nudge, put the detail in chat`});let c=ic(`${n||""}
|
|
762
|
+
${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 me()).find(w=>w.cwd&&(s===w.cwd||s.startsWith(w.cwd+Te.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(f=>f.name===l.leadBox),d=(f,w)=>String(f).replace(/\s+/g," ").trim().slice(0,w),p={title:n?d(n,jK):l.name,body:d(i,Py),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await C(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:p}),t.json({ok:!0,notification:p,delivered:Na?Na.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});S.post("/api/dictation",async(e,t)=>{if(!Buffer.isBuffer(e.body)||e.body.length===0)return t.status(400).json({error:"expected a raw audio body"});try{t.json(await FR(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});S.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await Cc(),terms:await Cy(),problem:await LR()})});S.post("/api/dictation/learn",async(e,t)=>{let{transcript:n,finalText:r}=e.body||{};if(typeof n!="string"||typeof r!="string")return t.status(400).json({error:"transcript and finalText are required"});t.json({learned:await BR(n,r)})});S.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await oe(e.params.id))return t.status(404).json({error:"track not found"});t.json(await UR())});S.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});if(n.archivedAt)return t.status(409).json({error:"track is archived"});t.json(await HR())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});S.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=e.body||{};t.json({ok:!0,...await WR(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});S.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c,circuit:l}=e.body||{};try{let u=await vd({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),d=null;if(c)try{await Aa(u,c,{source:"create"})}catch(f){d=f?.message||String(f)}let p=null;if(l)try{await Dd(u,await va(l),{source:"picker"})}catch(f){p=f?.message||String(f)}t.json({ok:!0,track:u,...d?{ticketWarning:d}:{},...p?{circuitWarning:p}:{}})}catch(u){let d=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(d)?400:500).json({error:d})}});S.get("/api/tracks/:id",async(e,t)=>{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:cO(xa(e.params.id)),subtasks:Zo(e.params.id).map(ol),pendingOffers:await Ow(n)})});S.delete("/api/tracks/:id",async(e,t)=>{try{let n=await zw(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Fg(e.params.id).catch(()=>{}),t.json(n)}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await Yo(e.params.id)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});S.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await Yw(e.params.id,e.body?.name)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/loop run Tracks/i.test(r)?409:/required|too long/i.test(r)?400:500;t.status(o).json({error:r})}});S.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await Td(e.params.id,{freshSession:e.body?.freshSession===!0})})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});S.get("/api/tracks/:id/messages",async(e,t)=>{let n=await yd(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});S.get("/api/tracks/:id/events",async(e,t)=>{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),t.flushHeaders();let r=i=>t.write(`id: ${i.seq}
|
|
759
763
|
data: ${JSON.stringify(i)}
|
|
760
764
|
|
|
761
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await
|
|
765
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await K(n))i.seq>o&&r(i);let s=bb(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
762
766
|
|
|
763
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});S.get("/api/tracks/:id/search",async(e,t)=>{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=
|
|
764
|
-
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Dt(e.params.id,
|
|
767
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});S.get("/api/tracks/:id/search",async(e,t)=>{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=vi(await K(n)),a=zl(s,r,{k:o,role:e.query.role||null,tag:e.query.tag||null});t.json({q:r,indexed:s.N,hits:a.map(i=>({score:Number(i.score.toFixed(3)),seq:i.chunk.seq,ts:i.chunk.ts,role:i.chunk.role,box:i.chunk.boxId,provider:i.chunk.provider,tags:i.chunk.tags,text:i.chunk.text}))})});S.get("/api/memory",async(e,t)=>{t.json({stores:await Db(),harvest:Cb()})});S.delete("/api/memory/:key/:file",async(e,t)=>{let n=String(e.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return t.status(400).json({error:"bad store key"});try{t.json({ok:!0,...await jb(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 BK=["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(`
|
|
768
|
+
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Dt(e.params.id,BK)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});S.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await Ad(e.params.id,{freshSession:e.body?.freshSession===!0})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:n?.transferredTo||/mid-turn|terminal|archived/i.test(r)?409:500;t.status(o).json({error:r,...n?.transferredTo?{transferredTo:n.transferredTo}:{}})}});S.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r,producer:o}=e.body||{};try{t.json(await Dt(e.params.id,n,{attachments:r,producer:typeof o=="string"?o:null}))}catch(s){let a=s?.message||String(s);if(s?.usageCap)return t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});if(s?.transferredTo)return t.status(409).json({error:a,transferredTo:s.transferredTo});t.status(/not found/i.test(a)?404:/archived/i.test(a)?409:/required|not running/i.test(a)?400:500).json({error:a})}});S.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(n.archivedAt)return t.status(409).json({error:"track is archived"});if(!n.cwd)return t.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(e.body)?e.body:null,o=await kE(n.cwd,{filename:e.query.filename,buffer:r});t.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);t.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});S.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(!n.cwd)return t.status(404).json({error:"track has no workspace"});let r=Te.basename(String(e.params.file||""));if(!r||!nw(Te.extname(r).slice(1)))return t.status(400).json({error:"not an upload path"});t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox");let o=tw(n.cwd),s,a;try{s=await wt.promises.realpath(o),a=await wt.promises.realpath(Te.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+Te.sep))return t.status(404).json({error:"upload not found"});t.sendFile(r,{root:o},i=>{i&&!t.headersSent&&t.status(404).json({error:"upload not found"})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});var LK=/^[0-9a-fA-F-]{8,64}$/;S.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});let{box:r,uuid:o}=e.params,s=Number(e.params.idx);if(!LK.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(p=>p.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=tn(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=AK.createInterface({input:wt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let p of u){if(!p.includes(c))continue;let f;try{f=JSON.parse(p)}catch{continue}if(f.uuid===o){l=f,u.close();break}}let d=l?wf(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",d.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(d.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await xd(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});S.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await Pw(e.params.id);t.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o}=e.body||{};try{t.json(await bd(e.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected/i.test(a)?409:500;t.status(i).json({error:a})}});S.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await Kw({provider:e.params.provider,fresh:e.body?.fresh===!0});t.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);t.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});S.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await Mw(e.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;t.status(a).json({error:s})}});S.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Fw(e.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;t.status(a).json({error:s})}});S.get("/api/tracks/:id/usage",async(e,t)=>{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=e.query.fresh==="1"||e.query.fresh==="true";t.json(await Qd(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Uw(e.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;t.status(s).json({error:o})}});S.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await lg(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:ol(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return t.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;t.status(c).json({error:i})}});S.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:Zo(e.params.id).map(ol)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Cw(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});S.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:ol($d(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});S.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:ol(ug(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function ol(e){return e?{id:e.id,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,step:e.step,running:!e.finishedAt,box:e.box,branch:e.branch,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}S.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await Ed(e.params.id,n,r!==!1,o);t.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);if(s?.usageCap)return t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});t.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});S.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await Bw(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});S.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(cO(xa(e.params.id))??{running:!1})});S.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await rg(e.params.id);t.json({ok:!0,step:n.step,startedAt:n.startedAt})}catch(n){let r=n?.message||String(n);t.status(/already running/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});S.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=ng(e.params.id);if(!n)return t.json({running:!1});t.json({running:!n.finishedAt,step:n.step,error:n.error,startedAt:n.startedAt,finishedAt:n.finishedAt,...n.result??{}})});S.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await _d(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:400).json({error:r})}});S.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await ag({...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 cO(e){return e?{running:!e.finishedAt,step:e.step,error:e.error,fromBox:e.fromBox,toBox:e.toBox,toAgent:e.toAgent,skipPermissions:e.skipPermissions,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}S.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:_l(),paths:Zb,trace:await Xb({limit:n})})});var Dy=Te.resolve(XR,"..","..","client","dist");wt.existsSync(Dy)&&(S.use(Ut.static(Dy)),S.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(Te.join(Dy,"index.html"),rl)));var lO=_K.createServer(S),FK=new RK({noServer:!0}),uO=(e,t,n)=>{if(!(Gc(e)||Ma.allowed(Ma.clientIp(e))||By.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
765
769
|
\r
|
|
766
|
-
`),t.destroy();return}if(
|
|
770
|
+
`),t.destroy();return}if(m$(e,t,n))return;if(!d$(e,Ly,{enforce:QR})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
767
771
|
\r
|
|
768
|
-
`),t.destroy();return}let r=
|
|
772
|
+
`),t.destroy();return}let r=yk(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
769
773
|
\r
|
|
770
|
-
`),t.destroy();return}let o=r.sessionName;if(!
|
|
774
|
+
`),t.destroy();return}let o=r.sessionName;if(!Be(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
771
775
|
\r
|
|
772
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
776
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;FK.handleUpgrade(e,t,n,i=>hk(i,o,{cols:s,rows:a}))};lO.on("upgrade",uO);var dO=process.env.BOSTRAT_CERT_DIR||Te.join(process.env.HOME||gp.tmpdir(),".bostrat","certs"),VR=Number(process.env.BOSTRAT_TLS_PORT)||3443,JR=Te.join(dO,"bostrat.globopi.key"),YR=Te.join(dO,"bostrat.globopi.crt");if(wt.existsSync(JR)&&wt.existsSync(YR)){let e=IK.createServer({key:wt.readFileSync(JR),cert:wt.readFileSync(YR)},S);e.on("upgrade",uO),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(VR,yp,()=>{console.log(`[bostrat-agent] https listening on ${yp||"0.0.0.0"}:${VR}`)})}lO.listen(qR,yp,()=>{console.log(`[bostrat-agent] listening on ${yp||"0.0.0.0"}:${qR}`);let e=v_(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: ${Rt}`),console.log(`[bostrat-agent] auto-trigger default: ${ZR}%`),console.log(`[bostrat-agent] ip allowlist: ${Ma.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(By.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),tg().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),ig().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Xw().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(()=>Zw()).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)),qw().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)),VK(),x_({listTracks:me}),WK(),qK(),zK(),yv({listTracks:me,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)),AE()&&EE({listTracks:me}),ek().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)),dR(),hR(),kI({resolveTrack:oe}),RI({listTracks:me,createTrack:vd,archiveTrack:Yo,listRepos:Wn,resolveTrack:oe,sayToLead:Dt,answerPrompt:bd,startLeadHandoff:Ed,interruptLead:xd,listSubtaskJobs:Zo,cancelSubtask:$d,readEvents:K,appendEvent:C,divertShip:Ay,answerPrExists:Ey,knownAgents:bc}),UK(),si().then(t=>{t||console.warn(`[boot] no host tmux server on ${Rt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),$R(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Na=C_({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:Ly,nodeId:process.env.BOSTRAT_NODE_ID||gp.hostname(),name:process.env.BOSTRAT_NODE_NAME||gp.hostname(),listTracks:me,detectAgents:gd,sampleMetrics:zI,deliverySample:()=>eA(Nw()),onHashKey:JT,onTracksTransferred:sg,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||gp.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(()=>{}),zR=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(()=>{}),zR=null},onConnected:()=>{uR()},onUpdateAvailable:async t=>{try{jy={latest:t.latest,current:t.current},await Bn({...t,policy:vt()}),vt()==="auto"&&await tO({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>NK(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:vt(),status:"available",reason:"refused"}),vt()!=="off"&&a){let i=await Pd({to:a,kind:Xt,reason:"hard-floor"});i.ok?(tl.cancel(),await Bn({current:t,latest:a,min:n,installKind:o,required:!0,policy:vt(),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:vt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),ks().catch(()=>{}),gE().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),xI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),UA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),oE().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),iS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Na?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function UK(){HK(),Qu().catch(()=>{}),setInterval(()=>Qu().catch(()=>{}),1e4)}function HK(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-roadmap","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-followup","bostrat-ship","bostrat-status","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=Te.resolve(XR,"..","bin",e),n=Te.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!wt.existsSync(t))continue;wt.mkdirSync(n,{recursive:!0});let r=Te.join(n,e);(wt.existsSync(r)?wt.readlinkSync(r):null)!==t&&(wt.rmSync(r,{force:!0}),wt.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 WK(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await me(),r=await YI({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 qK(){let e=null,t=async()=>{try{let n=Ps(),r=0,o=[],s=null,a=null;try{let u=await me(),d=await Xg(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await VI(u,{days:7}),a=(await Qg(u,{window:"7d"})).categorization}catch{}let i=[...n.problems,...a?.problems||[]];s&&!s.ok&&i.push(`${s.divergent} recent row(s) diverge from the table (implied-rate audit) \u2014 run \`npm run cost -- --audit\``);let c=o.filter(u=>u!=="unknown");c.length&&i.push(`${r} unpriced turn(s) \u2014 add rates for: ${c.join(", ")}`);let l=i.join(`
|
|
773
777
|
`);if(!i.length){e!==""&&console.log(`[pricing] table ok \u2014 rates verified ${n.tableVerifiedAt??"never"}`),e="";return}for(let u of i)console.warn(`[pricing] \u26A0 ${u}`);l!==e&&process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: cost instrument needs attention",Tags:"warning"},body:i.join(`
|
|
774
|
-
`).slice(0,900)}).catch(()=>{}),e=l}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var
|
|
778
|
+
`).slice(0,900)}).catch(()=>{}),e=l}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var KK=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,GK=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function zK(){let e=async()=>{try{let n=await Pb();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await me()).filter(a=>!Ve(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await Nb([...new Set(o)]);s.harvested&&console.log(`[memory] reconciler harvested ${s.harvested} fact(s) from ${o.length} live workspace(s)`)}catch{}};setTimeout(()=>{e(),setInterval(e,KK).unref()},GK).unref()}function VK(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await me();for(let n of t)Ve(n)||!n.boxes?.some(r=>r.state==="live")||(await yd(n.id).catch(()=>{}),await nd(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|