bostrat-node 1.8.2 → 1.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bostrat-followup +3 -0
- package/bin/bostrat-gmail +1 -1
- package/bin/bostrat-ship +2 -0
- package/dist/cli.js +11 -11
- package/dist/index.js +255 -216
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var a$=Object.defineProperty;var j=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Pd=(e,t)=>{for(var n in t)a$(e,n,{get:t[n],enumerable:!0})};import{createHash as i$,randomBytes as c$,timingSafeEqual as l$}from"node:crypto";function Ng(){return c$(32).toString("hex")}function va(e){let t=typeof e=="string"?e.trim():"";return t?i$("sha256").update(t).digest("hex"):null}function Pg(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:l$(Buffer.from(e),Buffer.from(t))}function Sa(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Dd.set(e,(Dd.get(e)||0)+1),Bd.has(e)&&n-Bd.get(e)<u$)return!1;Bd.set(e,n);let o=Object.entries(t).filter(([,s])=>s!=null&&s!=="").map(([s,a])=>`${s}=${a}`).join(" ");return r(`[hook-audit] ${e} n=${Dd.get(e)}${o?` ${o}`:""}`),!0}async function Dg(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 $n,Rn,Dd,Bd,u$,Yo=j(()=>{$n="BOSTRAT_HOOK_TOKEN",Rn="x-bostrat-hook";Dd=new Map,Bd=new Map,u$=6e4});import It from"node:fs/promises";import Bg from"node:os";import St from"node:path";import{execFile as d$}from"node:child_process";import{promisify as f$}from"node:util";function Nc(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function p$(){return process.env.CLAUDE_CONFIG_DIR?St.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):St.join(Bg.homedir(),".claude.json")}function m$(){return process.env.CLAUDE_CONFIG_DIR?St.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):St.join(Bg.homedir(),".claude","settings.json")}async function or(e){if(!e||typeof e!="string")return;let t=St.resolve(e),n=p$(),r=m$(),o={};try{o=JSON.parse(await It.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 It.mkdir(St.dirname(n),{recursive:!0}),await It.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await It.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 "${Rn}: $${$n}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let f of["SessionStart","UserPromptSubmit","Stop"])i.hooks[f]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await It.mkdir(St.dirname(r),{recursive:!0}),await It.writeFile(r,JSON.stringify(i,null,2)),await h$(t)}async function h$(e){try{let t=St.resolve(e),n=St.join(t,".bostrat","memory","claude"),r=St.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await It.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(St.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await It.mkdir(n,{recursive:!0}),await It.mkdir(St.dirname(r),{recursive:!0}),await It.writeFile(r,JSON.stringify(o,null,2)),await w$(t,["/.claude/settings.local.json"])}catch{}}async function w$(e,t){try{let{stdout:n}=await f$(d$)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=St.resolve(e,n.trim()),o=await It.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await It.mkdir(St.dirname(r),{recursive:!0}),await It.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var Ta=j(()=>{Yo()});import jd from"node:fs/promises";import g$ from"node:os";import Pc from"node:path";function y$(){let e=process.env.CODEX_HOME||Pc.join(g$.homedir(),".codex");return Pc.join(e,"config.toml")}function jg(e){return`[projects."${e}"]`}function b$(e,t){let n=jg(t),r=e.indexOf(n);if(r===-1)return!1;let o=e.slice(r+n.length),s=o.search(/^\s*\[/m),a=s===-1?o:o.slice(0,s);return/trust_level\s*=\s*"trusted"/.test(a)}async function Jo(e){if(!e||typeof e!="string")return;let t=Pc.resolve(e),n=y$(),r="";try{r=await jd.readFile(n,"utf8")}catch{}if(b$(r,t))return;let o=jg(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),f=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
|
-
`;r=r.slice(0,a)+
|
|
8
|
+
`;r=r.slice(0,a)+f+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await
|
|
14
|
-
`,"utf8")}var
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await jd.mkdir(Pc.dirname(n),{recursive:!0}),await jd.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Dc=j(()=>{});import Lg from"node:fs/promises";import k$ from"node:os";import Ea from"node:path";function x$(e){let n=Ea.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ea.join(k$.homedir(),".cursor");return Ea.join(r,"projects",n)}async function Xo(e){if(!e||typeof e!="string")return;let t=Ea.resolve(e),n=x$(t),r=Ea.join(n,".workspace-trusted");await Lg.mkdir(n,{recursive:!0}),await Lg.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var Bc=j(()=>{});import Fd from"node:fs/promises";import sr from"node:path";import{execFile as v$}from"node:child_process";import{promisify as S$}from"node:util";async function $$(e){try{return(await Fd.stat(sr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function R$(e){let t=i=>Fd.readFile(sr.join(e,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await t("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await t(`refs/heads/${i}`)||(o??=await t("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await t("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function On(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await $$(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:sr.basename(o),branch:await R$(o)});return}if(s>=_$)return;let i;try{i=await Fd.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||I$.has(c.name)||await t(sr.join(o,c.name),s+1)}for(let o of A$)await t(sr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await Se(o.path))?.toLowerCase();if(s){if(n.has(s))continue;n.add(s)}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function O$(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Se(e){if(!e)return null;if(Ld.has(e))return Ld.get(e);let t=null;try{let{stdout:n}=await T$("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=O$(n)}catch{t=null}return Ld.set(e,t),t}async function Zo(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await On())if((await Se(n.path))?.toLowerCase()===t)return n.path;return null}var T$,Fg,E$,A$,_$,I$,Ld,ar=j(()=>{T$=S$(v$),Fg=process.env.HOME||"/home/globo",E$=sr.join(process.env.BOSTRAT_STATE_DIR||sr.join(Fg,".bostrat"),"repos"),A$=[...(process.env.BOSTRAT_REPOS_ROOT||sr.join(Fg,"Documents")).split(","),E$].map(e=>e.trim()).filter(Boolean),_$=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,I$=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Ld=new Map});import C$ from"node:fs";import Ug from"node:fs/promises";import Aa from"node:path";import M$ from"node:os";import{fileURLToPath as N$}from"node:url";import{execFile as P$}from"node:child_process";import{promisify as D$}from"node:util";function Ie(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 jc(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Lc(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Wg.test(n))throw Qo("repo must be owner/name",400);let r=Ud.get(n);if(r&&r.expiresAt-Date.now()>L$)return r;if(!jc(t))throw Qo("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await qg(`${Ie(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Kg(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Qo(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if(o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Qo(s?.error||`plane replied ${o.status}`,o.status)}let a={token:s.token,expiresAt:Number(s.expiresAt)||Date.now()+55*6e4,username:s.username??null,installationId:s.installationId??null};return Ud.set(n,a),a}async function _a({env:e=process.env,force:t=!1}={}){if(!jc(e))return{configured:!1,repos:[],username:null};if(!t&&eo&&Date.now()-eo.at<F$)return eo.data;try{let n=await qg(`${Ie(e)}/v1/github/repos`,{headers:Kg(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return eo={at:Date.now(),data:r},r}catch(n){return eo?eo.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Gg(){Ud.clear(),eo=null}async function ln({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Se(e):null);if(!r)return{};let{token:o}=await Lc(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Hd({env:e=process.env}={}){return(await _a({env:e})).username??null}function q$(e){let[t,n]=String(e).split("/");return Aa.join(j$,t,n)}async function Wd(e,{env:t=process.env}={}){let n=(...r)=>Hg("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${H$()}" ${W$(t)}`),await n("config","credential.useHttpPath","true")}async function Fc(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Wg.test(String(e||"")))throw Qo(`bad repo slug "${e}"`,400);let r=q$(e);if(C$.existsSync(Aa.join(r,".git")))return r;let{token:o}=await Lc(e,{env:t});await Ug.mkdir(Aa.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Hg("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:U$,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Ug.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Qo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Wd(r,{env:t}),r}var Hg,B$,j$,Wg,L$,F$,U$,qg,Ud,eo,Kg,Qo,H$,W$,it=j(()=>{ar();Hg=D$(P$),B$=process.env.BOSTRAT_STATE_DIR||Aa.join(process.env.HOME||M$.tmpdir(),".bostrat"),j$=Aa.join(B$,"repos"),Wg=/^[^/\s]+\/[^/\s]+$/,L$=5*6e4,F$=6e4,U$=10*6e4,qg=fetch,Ud=new Map,eo=null;Kg=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Qo=(e,t)=>Object.assign(new Error(e),{status:t});H$=()=>N$(new URL("../bin/bostrat-git-credential",import.meta.url)),W$=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function zg(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Jg(e,t,n,r=Vg){let o=Ie(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await G$(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Xg(e,{env:t=process.env,force:n=!1,timeoutMs:r=Vg}={}){if(!zg(t))return{facts:[],source:"unconfigured"};let o=qd.get(e);if(!n&&o&&Date.now()-o.at<K$)return{facts:o.facts,source:"cache"};try{let s=await Jg(`/v1/memory/${encodeURIComponent(e)}`,{headers:Yg(t)},t,r);if(!s.ok)throw new Error(`plane ${s.status}`);let a=await s.json(),i=Array.isArray(a?.facts)?a.facts:[];return qd.set(e,{at:Date.now(),facts:i}),{facts:i,source:"plane"}}catch(s){return o?{facts:o.facts,source:"stale",error:s?.message}:{facts:[],source:"unreachable",error:s?.message}}}async function Uc(e,t,{env:n=process.env}={}){if(!zg(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Jg(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Yg(n),"content-type":"application/json"},body:JSON.stringify({facts:t})},n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.results)?o.results:[];return qd.delete(e),{pushed:s.filter(a=>a.outcome==="accepted").length,rejected:s.filter(a=>a.outcome==="rejected"),source:"plane"}}catch(r){return{pushed:0,source:"unreachable",error:r?.message}}}var K$,Vg,qd,G$,Yg,Zg=j(()=>{it();K$=6e4,Vg=5e3,qd=new Map,G$=(...e)=>fetch(...e);Yg=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Kd from"node:fs/promises";import es from"node:path";import V$ from"node:os";import{EventEmitter as z$}from"node:events";import{randomUUID as Y$}from"node:crypto";function ts(e){return e?.cwd?es.join(e.cwd,".bostrat","events"):es.join(Gd,"events")}async function Q$(e,t){if(!Hc.has(t)){let o=(await F(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Hc.set(t,o)}let n=Hc.get(t)+1;return Hc.set(t,n),n}function Vd(e,t){let n=Array.isArray(e?.epochs)?e.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(t))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=t)return r}return 0}}async function $(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!J$.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!X$.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!Z$.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?es.join(Gd,"events"):ts(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let u=e.boxes.find(f=>f.name===t.boxId);o=Vd(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await Q$(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||Y$(),trackId:n,boxId:t.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:t.ts??Date.now(),type:t.type,actor:t.actor??"system",provider:t.provider??null,source:t.source??"orchestrator",payload:t.payload??{},...t.causationId?{causationId:t.causationId}:{},...t.correlationId?{correlationId:t.correlationId}:{}},c=JSON.stringify(i),l=i;return ey(c)&&(c=ty(c),l=JSON.parse(c)),await Kd.mkdir(r,{recursive:!0}),await Kd.appendFile(Qg(r,n),c+`
|
|
15
|
+
`),ct.emit("event",l),ct.emit(`track:${n}`,l),l}async function F(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?es.join(Gd,"events"):ts(e));let o;try{o=await Kd.readFile(Qg(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(ey(a)?ty(a):a))}catch{}return s}function ny(e,t){let n=`track:${e}`;return ct.on(n,t),()=>ct.off(n,t)}var J$,X$,Z$,ct,Gd,Qg,Hc,eR,ey,ty,me=j(()=>{J$=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","memory.harvested","handoff.artifact","code.pushed","code.committed","code.rework","rework.cause.offered","rework.cause.answered","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","loop.create.offered","loop.create.answered","loop.created","loop.update.offered","loop.update.answered","loop.updated","loop.delete.offered","loop.delete.answered","loop.deleted","loop.paused","followup.armed","followup.armed.cancelled","followup.offered","followup.answered","followup.create.offered","followup.create.answered","followup.created","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),X$=new Set(["user","agent","orchestrator","system","peer"]),Z$=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),ct=new z$;ct.setMaxListeners(0);Gd=process.env.BOSTRAT_STATE_DIR||es.join(process.env.HOME||V$.tmpdir(),".bostrat");Qg=(e,t)=>es.join(e,`${t}.ndjson`),Hc=new Map;eR=/(?<!\\)((?:\\\\)*)\\u0000/g,ey=e=>e.includes("\\u0000"),ty=e=>e.replace(eR,"$1\\ufffd")});import Y from"node:fs/promises";import tR from"node:os";import we from"node:path";import{execFile as nR}from"node:child_process";import{promisify as rR}from"node:util";function qc(){return we.join(Kc,"memory","repos")}function to(e){return e===Cn?we.join(Kc,"memory",Cn):we.join(qc(),e)}async function $a(e){try{return JSON.parse(await Y.readFile(we.join(e,ly),"utf8"))}catch{return{}}}async function Yd(e,t){let n=await $a(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await Y.mkdir(e,{recursive:!0}),await Y.writeFile(we.join(e,ly),JSON.stringify(n))}function oy(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 sy(e){try{let{stdout:t}=await oR("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Jd(e){if(!e)return[];let t=oy(await sy(e));if(t)return[t];let n=[],r=[];try{r=await Y.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=oy(await sy(we.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Gc(e){let t=/^---\n([\s\S]*?)\n---\n?/.exec(e);return t?{fm:t[1],body:e.slice(t[0].length)}:{fm:null,body:e}}function ir(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function un(e,t=null){let n=t??await Y.readFile(e,"utf8").catch(()=>""),{fm:r}=Gc(n),o=ir(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await Y.stat(e)).mtimeMs}catch{return 0}}function uy(e,t){let n=ir(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Cn){let o=ir(e,"trust");if(!o||/^operator/i.test(o))return Cn}return t}function Xd(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
|
-
`)){let l=
|
|
18
|
+
`)){let l=rs(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=rs(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
22
|
-
`),o);if(a.length){let
|
|
23
|
-
`).filter(f
|
|
24
|
-
`)}await
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
21
|
+
`)}function dy(e,t){let n=ir(t,"name")||e.replace(/\.md$/,""),r=ir(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Ra(e){let t=[];try{t=await Y.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 fy(e,t){let n=we.join(e,".bostrat","memory"),r=await Ra(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=ay.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let f of r){let d=we.join(n,f);if(o)try{if((await Y.stat(d)).mtimeMs<o-aR)continue}catch{continue}let p;try{p=await Y.readFile(d,"utf8")}catch{continue}let{fm:h}=Gc(p),w=uy(h,t);if(!w)continue;let g=to(w),k=we.join(g,f),S=await un(d,p);if(l.has(g)||l.set(g,await $a(g)),(l.get(g)[f]||0)>=S)continue;let E=await un(k).catch(()=>0);if(await Y.access(k).then(()=>!0).catch(()=>!1)&&E>=S)continue;await Y.mkdir(g,{recursive:!0}),await Y.writeFile(k,p),c++;let B=a.get(g);B||a.set(g,B={won:new Set,generated:new Map}),B.won.add(f),B.generated.set(f,dy(f,h)),i.has(w)||i.set(w,new Set),i.get(w).add(f)}let u=await Y.readFile(we.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[f,d]of a){let p=we.join(f,"MEMORY.md"),h=await Y.readFile(p,"utf8").catch(()=>"");await Y.writeFile(p,Xd(h,u,d.won,d.generated))}return ay.set(e,s),{harvested:c,changedByKey:i}}async function py(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await Y.access(we.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await Jd(r),a=await fy(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 iR(){let e=we.join(Kc,"workspaces"),t=[];try{t=await Y.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return py(t.filter(n=>n.isDirectory()).map(n=>we.join(e,n.name)))}async function uR(e,t){let n=to(e),r=await $a(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!cR.test(l)||l==="MEMORY.md")continue;let u=we.join(n,l),f=await Y.access(u).then(()=>!0).catch(()=>!1),d=f?await un(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await Y.rm(u,{force:!0}),await Yd(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await Y.mkdir(n,{recursive:!0}),await Y.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=we.join(n,"MEMORY.md"),l=await Y.readFile(c,"utf8").catch(()=>""),u=Xd(l,[...s.values()].join(`
|
|
22
|
+
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
23
|
+
`).filter(d=>!f.has(rs(d))).join(`
|
|
24
|
+
`)}await Y.mkdir(n,{recursive:!0}),await Y.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Vc(e,t=null){let n=to(e),r=t?.length?t:await Ra(n);if(!r.length)return{pushed:0,source:"empty"};let o=lR(await Y.readFile(we.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=we.join(n,a),c;try{c=await Y.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await un(i,c),indexLine:o.get(a)??null})}return Uc(e,s)}async function dR(e,{timeoutMs:t}={}){let{facts:n,source:r}=await Xg(e,t?{timeoutMs:t}:{});n.length?await uR(e,n):r==="plane"&&await Vc(e)}function fR(e){let t=e.split(`
|
|
25
|
+
`);t.length>ry&&(t=t.slice(0,ry));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>sR&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function mR(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=we.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let f=to(u),d=await $a(f);for(let[g,k]of Object.entries(d)){let S=we.join(n,g);await Y.access(S).then(()=>!0).catch(()=>!1)&&await un(S)<=k&&(await Y.rm(S,{force:!0}),o++)}let p=await Ra(f);if(!p.length)continue;await Y.mkdir(n,{recursive:!0});for(let g of p){let k=we.join(f,g),S=we.join(n,g),E=await un(k),R=await un(S).catch(()=>0);await Y.access(S).then(()=>!0).catch(()=>!1)&&R>=E||(await Y.copyFile(k,S),r++)}let h=await Y.readFile(we.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===Cn?"## operator memory":`## repo: ${u}`;h.trim()&&s.push(t.length>1?`${w}
|
|
28
28
|
|
|
29
|
-
${
|
|
29
|
+
${h.trim()}`:h.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
|
|
30
30
|
|
|
31
31
|
`)+`
|
|
32
|
-
`,c=
|
|
33
|
-
`).filter(p=>!s.has(
|
|
34
|
-
`);await
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=fR(i);await Y.mkdir(n,{recursive:!0}),await Y.writeFile(we.join(n,"MEMORY.md"),c);let l=we.join(n,"claude");return await Y.mkdir(l,{recursive:!0}),await Y.writeFile(we.join(l,"MEMORY.md"),pR,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function my(e){try{let t=await Jd(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,Cn];return await Promise.all(n.map(r=>dR(r,{timeoutMs:hR}))),await mR(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function hy(){return{...Je,pending:ns.size,queued:zd.size}}function wy(e){if(Wc.has(e))return Wc.get(e);zd.add(e);let t=iy.then(async()=>{zd.delete(e),Wc.delete(e);let n=await Qd(e);return Je.runs++,Je.lastRunAt=Date.now(),n.error?(Je.errors++,Je.lastError=n.error):Je.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Wc.set(e,t),iy=t.then(()=>{}),t}function zc(e,{delayMs:t=wR}={}){if(!e||typeof e!="string")return;Je.scheduled++;let n=ns.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{ns.delete(e),wy(e)},t);r.unref?.(),ns.set(e,r)}async function Zd(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await Y.access(we.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=ns.get(e);r&&(clearTimeout(r),ns.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([wy(e),s])}finally{clearTimeout(o)}}async function gy(e){return Ia||(Ia=(async()=>{try{cy||(cy=!0,await yR());let{harvested:t,changedByKey:n}=await py(e);for(let[r,o]of n)await Vc(r,[...o]);return Je.reconciles++,Je.lastReconcileAt=Date.now(),Je.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return Je.errors++,Je.lastError=t?.message||String(t),{harvested:0,error:Je.lastError}}finally{Ia=null}})(),Ia)}function gR(){return we.join(Kc,"memory",".full-sweep-done")}async function yy(){let e=gR();try{let t=JSON.parse(await Y.readFile(e,"utf8"));return Je.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...Je.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await iR();for(let[o,s]of n)await Vc(o,[...s]);let r={at:Date.now(),harvested:t};return await Y.mkdir(we.dirname(e),{recursive:!0}),await Y.writeFile(e,JSON.stringify(r)),Je.backfill=r,{ran:!0,...r}}catch(t){return Je.errors++,Je.lastError=t?.message||String(t),{ran:!1,error:Je.lastError}}}async function yR(){let e=0,t=[];try{t=await Y.readdir(qc(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=we.join(qc(),n.name),o=to(Cn),s=new Set,a=new Map;for(let p of await Ra(r)){let h=we.join(r,p),w;try{w=await Y.readFile(h,"utf8")}catch{continue}let{fm:g}=Gc(w);if(uy(g,n.name)!==Cn)continue;let k=we.join(o,p),S=await un(k).catch(()=>0),E=await un(h,w);await Y.mkdir(o,{recursive:!0}),E>S&&await Y.writeFile(k,w),await Y.rm(h,{force:!0}),s.add(p),a.set(p,dy(p,g)),e++}if(!s.size)continue;let i=Date.now();await Yd(r,[...s].map(p=>[p,i])),await Uc(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=we.join(r,"MEMORY.md"),l=await Y.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(p=>!s.has(rs(p))).join(`
|
|
34
|
+
`);await Y.writeFile(c,u).catch(()=>{});let f=we.join(o,"MEMORY.md"),d=await Y.readFile(f,"utf8").catch(()=>"");await Y.writeFile(f,Xd(d,l,s,a))}return{moved:e}}async function by(){let e=[],t=[Cn];try{for(let n of await Y.readdir(qc(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=to(n),o=await Ra(r),s=await $a(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await Y.readFile(we.join(r,i),"utf8").catch(()=>""),{fm:l}=Gc(c);a.push({file:i,name:ir(l,"name")||i.replace(/\.md$/,""),description:ir(l,"description")||"",scope:ir(l,"scope")||null,modified:await un(we.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function ky(e,t){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(t)||t==="MEMORY.md")throw new Error("bad fact filename");let n=to(e),r=we.join(n,t);if(!await Y.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await Y.rm(r,{force:!0}),await Yd(n,[[t,s]]);let a=we.join(n,"MEMORY.md"),i=await Y.readFile(a,"utf8").catch(()=>"");await Y.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>rs(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await Uc(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Qd(e){try{let t=await Jd(e),{harvested:n,changedByKey:r}=await fy(e,t[0]||null);for(let[o,s]of r)await Vc(o,[...s]);return n>0&&await kR(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function kR(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,bR)}}await $({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var oR,Kc,ry,sR,Cn,ly,rs,ay,aR,cR,lR,pR,hR,wR,ns,zd,iy,Je,Wc,cy,Ia,bR,Oa=j(()=>{Zg();me();oR=rR(nR),Kc=process.env.BOSTRAT_STATE_DIR||we.join(process.env.HOME||tR.tmpdir(),".bostrat"),ry=200,sR=25*1024;Cn="operator";ly=".tombstones.json";rs=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;ay=new Map,aR=2e3;cR=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,lR=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=rs(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};pR=`# 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
|
+
`;hR=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;wR=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,ns=new Map,zd=new Set,iy=Promise.resolve(),Je={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Wc=new Map;cy=!1,Ia=null;bR=20});import ef from"node:fs/promises";import xR from"node:path";async function Tt(e,t,{mode:n}={}){await ef.mkdir(xR.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++vR}.tmp`;await ef.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await ef.rename(r,e)}var vR,cr=j(()=>{vR=0});import Ey from"node:fs/promises";import Ay from"node:os";import _y from"node:path";function Ne(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Na(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),SR);try{return await $R(`${Ie(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function $y(){return Jc||(tf||=Ey.readFile(Iy(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Jc={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>tf=null),tf)}async function Xc(){await Tt(Iy(),Jc).catch(()=>{})}function Sy(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function rf(e){let t=await $y(),n=Sy(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=>Sy(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>xy&&(t.ops=t.ops.slice(-xy)),await Xc()}function Ty(){Yc||(Yc=setTimeout(()=>{Yc=null,os().catch(()=>{})},RR),Yc.unref?.())}function nf(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function CR(e,t=process.env){if(e.op==="record"){let n=await Na(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Ma(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:IR()}]})},t);if(!n.ok)throw nf(n.status);let o=(await n.json().catch(()=>null))?.results?.[0];return o?.outcome==="rejected"?{delivered:!1,rejected:o.reason}:{delivered:!0}}if(e.op==="blob"){let n=await Ey.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Na(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Ma(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw nf(r.status);return r.status===400?{delivered:!1,rejected:(await r.json().catch(()=>null))?.error||"rejected"}:{delivered:!0}}if(e.op==="delete"){let n=await Na(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Ma(t)},t);if(!n.ok)throw nf(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function os({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Ne(e)?Ca||(Ca=(async()=>{let r=await $y();MR(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await CR(a,e)}catch(c){if(!c?.refused)return Ty(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<ER)return Ty(),await Xc(),{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>vy&&(r.parked=r.parked.slice(-vy)),s++,await Xc();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await Xc()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Ca=null}),Ca):{flushed:0,source:"unconfigured"}}function MR(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=OR);if(n.length){e.parked=e.parked.filter(r=>!n.includes(r));for(let r of n)e.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function wt(e,t,{trackId:n=null,env:r=process.env}={}){return Ne(r)?(await rf({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),os({env:r})):{queued:!1,source:"unconfigured"}}async function lr(e,t,n,r,o,{env:s=process.env}={}){return Ne(s)?(await rf({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),os({env:s})):{queued:!1,source:"unconfigured"}}async function Vt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Ne(r)?(await rf({op:"delete",family:e,id:t,deletedAt:n}),os({env:r})):{queued:!1,source:"unconfigured"}}async function et(e,{env:t=process.env}={}){if(!Ne(t))return{records:[],source:"unconfigured"};try{let n=await Na(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Ma(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 Pa(e,t,n,r,{env:o=process.env}={}){if(!Ne(o))return null;try{let s=await Na(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ma(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 SR,TR,xy,ER,vy,AR,_R,Iy,IR,$R,RR,OR,Ma,Jc,tf,Yc,Ca,ur=j(()=>{it();cr();SR=1e4,TR=3e4,xy=2e3,ER=5,vy=100,AR=60*6e4,_R=()=>process.env.BOSTRAT_STATE_DIR||_y.join(process.env.HOME||Ay.tmpdir(),".bostrat"),Iy=()=>_y.join(_R(),"content-outbox.json"),IR=()=>process.env.BOSTRAT_NODE_ID||Ay.hostname(),$R=(...e)=>fetch(...e),RR=TR,OR=AR;Ma=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Jc=null,tf=null,Yc=null,Ca=null});import of from"node:fs/promises";import Ry from"node:path";async function Oy(e,t,{box:n=null,agent:r=null}={}){if(!e||!Ne())return{pushed:!1};try{let o=Date.now();return await wt("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await lr("checkpoints",e,o,"md",t),Da=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function jR(){if(Da&&Date.now()-Da.at<BR)return{records:Da.records,source:"plane"};let e=await et("checkpoints");return e.source==="plane"&&(Da={at:Date.now(),records:e.records}),e}async function Zc(e){try{let t=DR(e);if(!t||!Ne())return{restored:!1};let n=Ry.join(e,NR,PR);if(await of.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await jR();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 Pa("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await of.mkdir(Ry.dirname(n),{recursive:!0}),await of.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var NR,PR,DR,BR,Da,Qc=j(()=>{ur();NR=".bostrat/handoff",PR="latest.md",DR=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;BR=6e4,Da=null});import Ba from"node:fs/promises";import LR from"node:os";import My from"node:path";function no(e){let t=String(e??"");for(let n of FR)t=t.replace(n,"\u2039redacted\u203A");return t}function el(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function UR(e){try{await Ba.mkdir(sf,{recursive:!0}),await Ba.appendFile(ss,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await Ba.readFile(ss,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>Cy&&await Ba.writeFile(ss,`${n.slice(-Cy).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=
|
|
49
|
-
`,r=
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${ss}: ${t?.message||t}`)}}async function re(e={}){let t={at:new Date().toISOString(),ts:Date.now(),...e},n=[t.flow||"handoff",t.phase||t.step||"?",t.trackId?`track=${t.trackId}`:"",t.sessionName?`box=${t.sessionName}`:"",t.error?`ERR=${t.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),t.sessionName&&el())try{t.paneTail=no((await Ce(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(el()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch|submit-|gate-accept|blocked|timeout|dialog)/.test(String(t.phase||t.step||"")))&&await UR(t)}async function Ny({limit:e=80}={}){try{return(await Ba.readFile(ss,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=no(o.paneTail)),o}catch{return{raw:no(r)}}})}catch{return[]}}var FR,sf,ss,Cy,Py,dn=j(()=>{lt();FR=[/[A-Za-z0-9_-]{20,}#[A-Za-z0-9_-]{20,}/g,/\bsk-[A-Za-z0-9_-]{16,}/g,/\bghs_[A-Za-z0-9]{16,}|\bghp_[A-Za-z0-9]{16,}|\bgithub_pat_[A-Za-z0-9_]{20,}/g,/\bFlyV1 [A-Za-z0-9+/=_-]{16,}/g,/\bey[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g];sf=process.env.BOSTRAT_DEBUG_DIR||My.join(LR.homedir(),".bostrat","debug"),ss=My.join(sf,"handoff.jsonl"),Cy=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;Py={DEBUG_DIR:sf,LOG_FILE:ss}});import tl from"node:fs/promises";import af from"node:path";import HR from"node:os";async function By(e=Dy){await tl.mkdir(e,{recursive:!0});for(let t of qR){let n=t==="prepare-commit-msg"?KR:t==="post-commit"?GR:`${cf}exit 0
|
|
49
|
+
`,r=af.join(e,t),o=null;try{o=await tl.readFile(r,"utf8")}catch{}o!==n&&await tl.writeFile(r,n,{mode:493}),await tl.chmod(r,493).catch(()=>{})}return e}function jy({nodeUrl:e,dir:t=Dy,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",`${ja}=${e}`]}function Ly(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var ja,WR,Dy,qR,cf,KR,GR,lf=j(()=>{Yo();ja="BOSTRAT_NODE_URL",WR=process.env.BOSTRAT_STATE_DIR||af.join(process.env.HOME||HR.tmpdir(),".bostrat"),Dy=af.join(WR,"git-hooks"),qR=["applypatch-msg","pre-applypatch","post-applypatch","pre-commit","pre-merge-commit","prepare-commit-msg","commit-msg","post-commit","pre-rebase","post-checkout","post-merge","pre-push","post-rewrite","pre-auto-gc"],cf=`#!/bin/sh
|
|
50
50
|
# bostrat git hook shim (agent/src/git-hooks.js) \u2014 chains to the repo's own hook,
|
|
51
51
|
# then stamps/reports for rework attribution when running inside a bostrat box.
|
|
52
52
|
hook=$(basename "$0")
|
|
@@ -61,73 +61,73 @@ esac
|
|
|
61
61
|
if [ -x "$orig/$hook" ] && [ "$orig/$hook" != "$0" ]; then
|
|
62
62
|
"$orig/$hook" "$@" || exit $?
|
|
63
63
|
fi
|
|
64
|
-
`,
|
|
64
|
+
`,KR=`${cf}
|
|
65
65
|
# \u2500\u2500 bostrat: append provenance trailers (Layer 0). Skipped for merge commits
|
|
66
66
|
# (nobody "wrote" a merge) and outside a box (no token / no node url).
|
|
67
|
-
[ -z "$${
|
|
68
|
-
[ -z "$${
|
|
67
|
+
[ -z "$${$n}" ] && exit 0
|
|
68
|
+
[ -z "$${ja}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${Rn}: $${$n}" "$${ja}/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
|
+
`,GR=`${cf}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
|
-
[ -z "$${
|
|
79
|
-
[ -z "$${
|
|
78
|
+
[ -z "$${$n}" ] && exit 0
|
|
79
|
+
[ -z "$${ja}" ] && 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 "$${ja}/api/git/committed" \\
|
|
83
|
+
-H 'content-type: application/json' -H "${Rn}: $${$n}" --data-binary @- >/dev/null 2>&1 || true
|
|
84
84
|
exit 0
|
|
85
|
-
`});import{execFile as
|
|
86
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,
|
|
87
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
88
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
89
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function
|
|
90
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function
|
|
91
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
92
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function
|
|
93
|
-
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(
|
|
94
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function
|
|
95
|
-
`)){let p=
|
|
96
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function
|
|
97
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let
|
|
98
|
-
`).trim():"";
|
|
99
|
-
`).trim():""}function
|
|
100
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let w of l.content){let
|
|
101
|
-
|
|
102
|
-
`):""}var
|
|
103
|
-
`);if(!
|
|
104
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
105
|
-
`);return t}function
|
|
106
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
107
|
-
`).pop()||"";return
|
|
108
|
-
`),o=r.findIndex(i=>
|
|
109
|
-
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function
|
|
110
|
-
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function
|
|
111
|
-
`),n=-1;for(let s=t.length-1;s>=0;s--)if(t[s].trim()){n=s;break}if(n<0)return!1;let r=t[n];if(
|
|
112
|
-
`),r=-1;for(let o=n.length-1;o>=0;o--)if(
|
|
113
|
-
`)}function
|
|
114
|
-
`))
|
|
115
|
-
`).slice(-
|
|
116
|
-
`);return
|
|
85
|
+
`});import{execFile as Uy}from"node:child_process";import{promisify as VR}from"node:util";import rl from"node:fs";import{randomUUID as Hy}from"node:crypto";function cs(e){return new Promise(t=>setTimeout(t,e))}function nl(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 Wy(e){return`/tmp/tmux-${e}/default`}function zR({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=Wy(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Fa(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${La}=1`]}function Ua(e,t,n){let r=ro(e),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(e);if(!r||o)return e;let s=`${e} --session-id ${t}`,a=/(^|\s)(--name|-n)(\s|=)/.test(e);return n&&!a&&Pe(n)&&(s+=` --name ${n}`),s}function ro(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function dr(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function XR(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:ro(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function qy(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await Ky(e,{loginShell:t});return r===ol?!0:r===as?!1:n}async function Ky(e,{loginShell:t=!1,timeoutMs:n=uf}={}){if(!e)return as;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await $e(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),ol}catch(s){return s?.killed||s?.signal?is:s?.code===1||s?.code===127?as:is}}async function sl(e,{retries:t=ZR,sleep:n=QR,probe:r=Ky}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:uf});for(let a=0;s===is&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:uf*2});return s}function V(...e){return["-S",$t,...e]}async function fr(){if(!rl.existsSync($t))return!1;try{return await $e(K,V("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ha(){if(await fr())return!0;try{rl.mkdirSync($t.replace(/\/[^/]+$/,""),{recursive:!0}),await $e(K,V("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await fr()}function Pe(e){return typeof e=="string"&&eO.test(e)}async function zt(){if(!rl.existsSync($t))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 $e(K,V("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,p,h]=r.split("|"),w=Number(i),g=Number(h);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!JR.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ge(e){return(await zt()).find(n=>n.name===e)||null}function rO(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=nO&&tO.test(t)}function al(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Gy(e){try{let{stdout:t}=await $e(K,V("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function aO(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[];for(let o=1;o<=t.length-3;o++)n.push(t.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function iO(e,t){let n=Date.now()+Wa(),r=aO(t),o=!1,s=0;for(;;){if(await cs(oO),s+=1,!await Gy(e))return;let a=await Ce(e,{lines:15}),i=!o&&sO.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Mn(e))return;if(i){o=!0,await re({flow:"spawn",phase:"launch-retry",sessionName:e,agent:dr(t),promptHead:t.slice(0,120)}).catch(()=>{}),await $e(K,V("send-keys","-t",e,"C-u")),await $e(K,V("send-keys","-t",e,"-l",t)),await $e(K,V("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await re({flow:"spawn",phase:"launch-failed",sessionName:e,agent:dr(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function pr(e,t=YR,n={}){if(!rO(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=al(r);if(!Pe(o))throw new Error("resulting tmux session name is invalid");if(!await Ha())throw new Error(`host tmux server not running on ${$t} and auto-start failed \u2014 start one: ${K} -S ${$t} new-session -d -s keeper "sleep 2147483647"`);if(await Gy(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||df,a=n.agent||dr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Hy(),l=i?s:Ua(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&ro(s),d=n.firstRun===!0;d||(f&&await or(t),a==="codex"&&await Jo(t),a==="cursor"&&await Xo(t),await my(t),n.freshWorkspace!==!0&&await Zc(t));let p=f&&!d?Nc():null,h=Ng(),w=["-e",`${$n}=${h}`];w.push(...jy({nodeUrl:Ly()})),p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...Fa());try{await $e(K,V("new-session","-d","-s",o,"-c",t,...w))}catch(g){throw nl(p?nl(g,p):g,h)}return await oo(o),await $e(K,V("set-option","-t",o,"@bostrat-session","1")),await $e(K,V("set-option","-t",o,"@bostrat-title",r)),await $e(K,V("set-option","-t",o,"@bostrat-sid",c)),await $e(K,V("set-option","-t",o,"@bostrat-agent",a)),await $e(K,V("send-keys","-t",o,l,"Enter")),XR(l)&&iO(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:h}}async function Yt(e){if(!Pe(e))throw new Error("invalid session name");await $e(K,V("kill-session","-t",e))}async function Vy(e,t){if(Pe(e))try{let n=t?V("set-option","-t",e,"@bostrat-sid",t):V("set-option","-u","-t",e,"@bostrat-sid");await $e(K,n)}catch{}}async function Ce(e,{lines:t=40}={}){if(!Pe(e))return"";try{let n=V("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await $e(K,n);return String(r||"")}catch{return""}}async function zy(e){if(!Pe(e))return 0;try{let{stdout:t}=await $e(K,V("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
88
|
+
`).filter(Boolean).length}catch{return 0}}async function oo(e,{cols:t=uO,rows:n=dO}={}){if(!Pe(e))return!0;if(await zy(e))return!1;try{let r=await Yy(e);if(r.cols===t&&r.rows===n)return!0;await $e(K,V("resize-window","-t",e,"-x",String(t),"-y",String(n))),await $e(K,V("set-window-option","-t",e,"window-size","latest")),await cs(80)}catch{}return!0}async function fO(e){if(!Pe(e))return 0;try{let{stdout:t}=await $e(K,V("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Mn(e){let t=await fO(e);if(!t)return!1;try{return await $e("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function Yy(e){if(!Pe(e))return{cols:0,rows:0};try{let{stdout:t}=await $e(K,V("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 il(e,{cols:t=cO,rows:n=lO}={}){if(Pe(e)&&!await zy(e))try{let r=await Yy(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await $e(K,V("resize-window","-t",e,"-x",String(o),"-y",String(s))),await $e(K,V("set-window-option","-t",e,"window-size","latest")),await cs(80))}catch{}}async function Nn(e){return await il(e),Ce(e,{lines:Xe})}async function Le(e,t,{settleMs:n=40,kind:r}={}){if(!Pe(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>pO.has(s));for(let s=0;s<o.length;s++)await $e(K,V("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await cs(n);return re({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function ls(e,t,{kind:n}={}){if(!Pe(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await $e(K,V("send-keys","-t",e,"-N",String(r),"BSpace")),re({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ll(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Pe(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await $e(K,V("send-keys","-t",e,"-l",s)),n>0&&await cs(n);re({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function mO(e,t){return new Promise((n,r)=>{let o=Uy(K,V(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function hO(e,t){let n=`bostrat-${Hy().slice(0,8)}`;await mO(["load-buffer","-b",n,"-"],t),await $e(K,V("paste-buffer","-p","-d","-b",n,"-t",e))}async function Ze(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!Pe(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await hO(e,t):await $e(K,V("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await cs(a),await $e(K,V("send-keys","-t",e,"Enter"))}re({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r,...s?{paste:!0}:{}}).catch(()=>{})}var $e,K,Fy,$t,YR,df,La,ff,JR,ol,as,is,uf,ZR,QR,pf,eO,tO,nO,Wa,oO,sO,cO,lO,Xe,uO,dO,pO,cl,lt=j(()=>{Ta();Dc();Bc();Oa();Qc();dn();Yo();lf();$e=VR(Uy);K=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";Fy=typeof process.getuid=="function"?process.getuid():0,$t=zR({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return rl.existsSync(Wy(Fy))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Fy}),YR=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",df=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",La="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";ff="bostrat-usage-probe",JR=new Set([ff]);ol="present",as="absent",is="unknown",uf=5e3,ZR=[250,1e3,3e3],QR=e=>new Promise(t=>setTimeout(t,e));pf={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash",gh:"apt install gh (Debian: enable cli.github.com/packages) / brew install gh \u2014 see https://github.com/cli/cli#installation"};eO=/^[A-Za-z0-9_.-]{1,64}$/;tO=/^[A-Za-z0-9 ._↻-]+$/,nO=64;Wa=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,oO=1e3,sO=/not found|Unknown command/i;cO=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,lO=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Xe=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,uO=Number(process.env.BOSTRAT_BOX_COLS)||120,dO=Number(process.env.BOSTRAT_BOX_ROWS)||40;pO=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);cl=1600});import qa from"node:fs";import xO from"node:fs/promises";import us from"node:path";import vO from"node:os";function wf(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function gf(e){let t=us.join(hf,wf(e)),n;try{n=qa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=us.join(t,o);try{let{mtimeMs:a}=qa.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Jt(e,t){if(!t)return null;let n=us.join(hf,wf(e),`${t}.jsonl`);return qa.existsSync(n)?n:null}function nb(e){if(!e)return[];let t=us.join(hf,wf(e)),n;try{n=qa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=qa.statSync(us.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Et(e,t=131072){let n=await xO.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 TO(e){let t=e.split(`
|
|
89
|
+
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function Ka(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=Jt(e,t),o=!0,!r)return null}else if(n){if(r=gf(e),o=!1,!r)return null}else return null;let s;try{s=TO(await Et(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(tb,s.model),i=tb[s.model]||SO;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 tb,SO,hf,Xt=j(()=>{tb={"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},SO=2e5,hf=process.env.BOSTRAT_CLAUDE_PROJECTS||us.join(vO.homedir(),".claude","projects")});function EO(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){n+=s,s===r&&e[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&e[o+1]==="&"){t.push(n),n="",o++;continue}if(s==="|"&&e[o+1]==="|"){t.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
|
|
90
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function Ga(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 Va(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 Oe(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(sb(o)){t.has(o)&&r++;continue}n.push(o)}return n}function ab(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 IO(e){let t=ab(e),n=ut(Oe(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${Fe(n)}`:`Read lines ${t.from}\u2013${t.to} of ${Fe(n)}`:e.includes("-i")&&n?`Edit ${Fe(n)}`:null}function yf(e){if(e.includes("--files")){let s=ut(Oe(e,rb)[0]);return s&&s!=="."?`List files in ${ds(s)}`:"List files"}let t=Oe(e,rb),n=t[0];if(!n)return null;let r=ut(t.slice(1).find(s=>s!==".")),o=fs(n,40);return r?`Search for \u201C${o}\u201D in ${ds(r)}`:`Search for \u201C${o}\u201D`}function bf(e){let t=Oe(e,new Set(["--prefix"])),n=t[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?t[1]?`Run ${t[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function kf(e,t){if(e.includes("-e")||e.includes("--eval")||e.includes("-c")||e[1]==="-"||e.includes("<<"))return`Run an inline ${t} script`;if(e.includes("--test"))return"Run tests";let n=Oe(e)[0];return n?`Run ${Fe(n)}`:null}function $O(e){let t=Oe(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 ob(e){return e.find(t=>/^https?:\/\//.test(t))||null}function CO(e){let t=Va(Ga(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Fe(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return xf(t[2]);let n=Fe(t[0]);if(OO.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=RO[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function xf(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
91
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=EO(t);for(let i=0;i<r.length-1;i++){let c=Va(Ga(r[i])),l=Va(Ga(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Fe(c[0]))&&Fe(l[0])==="sed"){let u=ab(l),f=Oe(c)[0];if(u&&f)return fs(u.from===u.to?`Read line ${u.from} of ${Fe(f)}`:`Read lines ${u.from}\u2013${u.to} of ${Fe(f)}`,88)}}let o=[];for(let i of r){let c=CO(i);c&&o.push({d:c,head:Fe(Va(Ga(i))[0]||""),sub:Oe(Va(Ga(i)))[0]})}if(!o.length)return null;let s=o.find(i=>i.head==="git"&&i.sub==="commit"),a=o.find(i=>i.head==="git"&&i.sub==="push");return s&&a?"Commit and push changes":fs(s?s.d:o[0].d,88)}var fs,ut,Fe,ds,sb,rb,AO,_O,RO,OO,ib=j(()=>{fs=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,ut=e=>e&&!/[$`]/.test(e)?e:null,Fe=e=>e.replace(/\/+$/,"").split("/").pop()||e,ds=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},sb=e=>e.startsWith("-"),rb=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);AO={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 ${Fe(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"},_O={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"};RO={git:e=>{let t=Oe(e,new Set(["-C","-c"])),n=AO[t[0]];return n?n(t,e):null},gh:e=>{let t=Oe(e),n=_O[t[0]];return n?n(t):null},sed:IO,rg:yf,grep:yf,egrep:yf,nl:e=>{let t=ut(Oe(e)[0]);return t?`Read ${Fe(t)}`:null},cat:e=>{let t=ut(Oe(e)[0]);return t?`Read ${Fe(t)}`:null},head:e=>{let t=ut(Oe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Fe(t)}`:null},tail:e=>{let t=ut(Oe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Fe(t)}`:null},wc:e=>{let t=ut(Oe(e)[0]);return t?`Count lines in ${Fe(t)}`:"Count input lines"},ls:e=>{let t=ut(Oe(e)[0]);return t&&t!=="."?`List files in ${ds(t)}`:"List files"},find:e=>ut(e[1])&&!sb(e[1])&&e[1]!=="."?`Find files in ${ds(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=ut(Oe(e)[0]);return t?`Create directory ${ds(t)}`:null},touch:e=>{let t=ut(Oe(e)[0]);return t?`Create ${Fe(t)}`:null},rm:e=>{let t=ut(Oe(e)[0]);return t?`Delete ${ds(t)}`:null},mv:e=>{let t=ut(Oe(e)[0]);return t?`Move ${Fe(t)}`:null},cp:e=>{let t=ut(Oe(e)[0]);return t?`Copy ${Fe(t)}`:null},chmod:e=>{let t=ut(Oe(e)[1]);return t?`Change permissions on ${Fe(t)}`:null},diff:e=>{let t=Oe(e);return t.length>=2?`Compare ${Fe(t[0])} and ${Fe(t[1])}`:null},curl:e=>{let t=ob(e);return t?`Fetch ${fs(t,56)}`:"Fetch a URL"},wget:e=>{let t=ob(e);return t?`Fetch ${fs(t,56)}`:"Fetch a URL"},npm:bf,pnpm:bf,yarn:bf,npx:e=>{let t=Oe(e)[0];return t?`Run ${t}`:null},node:e=>kf(e,"Node"),python:e=>kf(e,"Python"),python3:e=>kf(e,"Python"),make:e=>{let t=Oe(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:$O,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=Oe(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=Oe(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Oe(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},OO=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function ps(e){let t=e.split(`
|
|
92
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function mr(e,t=""){let n=t.trim();return n||xf(e)||ps(e)}function vf(e,t){if(e){if(Array.isArray(e)){for(let n of e)vf(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Sf.has(e.name)){let n=String(e.input?.command||"").trim();n&&t.push({id:e.id||null,tool:e.name,command:n,description:String(e.input?.description||"").trim()})}for(let n of Object.values(e))vf(n,t)}}}async function cb(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await Et(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
93
|
+
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(vf(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:mr(f.command,f.description),ts:u})}return o.slice(-n)}function lb(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((h,w)=>w>=s&&(h.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.map(h=>({...h,n:i(h)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:p,ts:Math.max(...p.map(h=>h.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var Sf,ms=j(()=>{Xt();ib();Sf=new Set(["Bash"])});import ub from"node:fs";import Ef from"node:fs/promises";import Tf from"node:path";import MO from"node:os";async function db(e){try{return await Ef.realpath(e)}catch{return e}}function PO(e=NO){let t=[],n=r=>{let o;try{o=ub.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Tf.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{t.push({id:i?i[1]:null,file:a,mtimeMs:ub.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function DO(e,t=1048576){let n;try{n=await Ef.open(e,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(t,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
|
|
94
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function BO(e){let t=await DO(e);if(!t)return null;let n;try{n=JSON.parse(t)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function so(e,t){if(!e&&!t)return null;let n=PO();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await db(e);for(let o of n){let s=await BO(o.file);if(s&&(s===e||await db(s)===r))return o}return null}async function fb(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await so(e,t);if(!o)return[];let s;try{s=await Et(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
95
|
+
`)){let p=d.trim();if(!p||p[0]!=="{")continue;let h;try{h=JSON.parse(p)}catch{continue}if((h.type==="turn_context"&&h.payload?.model||h.type==="session_meta"&&h.payload?.model)&&(i=h.payload.model),h.type==="event_msg"){let S=h.payload?.type;S==="task_started"?(l=!0,u=!1,f=h.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&h.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:ao,sourceId:h.payload?.turn_id||f||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i}));continue}if(h.type!=="response_item")continue;let w=h.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,Af.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let g=LO(w.content),k=c.length?c.slice():null;c=[],!(!g&&!k)&&a.push({role:"assistant",text:g,tools:k||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}function FO(e){if(typeof e?.cmd=="string"&&e.cmd.trim())return e.cmd.trim();let t=e?.command;return typeof t=="string"&&t.trim()?t.trim():Array.isArray(t)&&t.length?t.length===3&&/^(ba)?sh$/.test(t[0])&&t[1]==="-lc"?String(t[2]).trim():t.map(String).join(" ").trim():null}function UO(e){let t=e[0];if(t!=='"'&&t!=="'"&&t!=="`")return null;let n="";for(let r=1;r<e.length;r++){let o=e[r];if(o==="\\"){let s=e[++r];if(s===void 0)return null;if(s==="n")n+=`
|
|
96
|
+
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function HO(e){let t=String(e||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(t);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=t.slice(s,a+1<n.length?n[a+1]:t.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=UO(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function pb(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await so(e,t);if(!o)return[];let s;try{s=await Et(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
97
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let f=[];if(u.type==="function_call"&&za.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let h=FO(p);h&&(f=[h])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=HO(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,h)=>{a.push({id:d?h?`${d}:${h}`:d:null,tool:u.name,command:p,description:"",summary:mr(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function mb(e,t){let n=await so(e,t);if(!n)return null;try{return Math.round((await Ef.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var NO,jO,LO,za,Af,ao,io=j(()=>{Xt();ms();NO=process.env.BOSTRAT_CODEX_SESSIONS||Tf.join(process.env.CODEX_HOME||Tf.join(MO.homedir(),".codex"),"sessions");jO=new Set(["output_text","text"]),LO=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&jO.has(t.type)).map(t=>t.text).join(`
|
|
98
|
+
`).trim():"";za=new Set(["exec_command","shell","exec"]),Af=new Set(["wait","write_stdin"]),ao="Codex ended the turn without a response \u2014 it hit an error the CLI shows only in the terminal (e.g. quota or billing). Check the Terminal tab."});function Ya({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&hb.has(r))return hb.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 wb(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Ya({error:t.code,status:t.status,text:e.text}),code:t.code||null,status:t.status||null,requestId:t.requestId||null}}if(e.failed){let t=String(e.text||"");return{cls:t===ao?"other":Ya({text:t}),code:null,status:null,requestId:null}}return null}function gb(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Ya({text:n}),code:null,status:WO(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function WO(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var k5,hb,dl=j(()=>{io();k5=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),hb=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 qO(e,t){return!e||!t?null:Jt(e,t)}function KO(e){return typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
99
|
+
`).trim():""}function GO(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 VO(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function _f(e){let t=[],n=e?.message?.content;if(!Array.isArray(n))return t;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)t.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&t.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return t}function yb(e,t){if(!e.uuid)return null;let n=_f(e).map((r,o)=>({uuid:e.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&t.get(r.toolUseId)||null}));return n.length?n:null}async function If(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await Et(e,t)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
100
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let w of l.content){let g=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof g=="string"&&zO.test(g)&&s.set(w.id,g.split("/").pop())}if(VO(l.content)){let w=yb(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=KO(l.content),d=u==="assistant"?GO(l.content):null,p=u==="user"?yb(c,s):null;if(!f&&!d&&!p)continue;let h=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:Ya({error:c.error,status:c.apiErrorStatus,text:f})}:null;o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:h||void 0})}return o.slice(-n)}async function bb(e,t,{turns:n=16}={}){let r=qO(e,t);if(!r)return"";let o=await If(r,{limit:n});return o.length?o.map(s=>{let a=s.role==="user"?"User":"Lead agent",i=s.tools?`_(also ran: ${s.tools.join(", ")})_`:"",c=s.images?.length?"(viewed an image)":"(took an action)";return`**${a}:** ${YO(s.text||c,700)} ${i}`.trim()}).join(`
|
|
101
|
+
|
|
102
|
+
`):""}var zO,YO,fl=j(()=>{dl();Xt();zO=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;YO=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import JO from"node:fs/promises";import{execFile as XO}from"node:child_process";import{promisify as ZO}from"node:util";function Sb(e){Rf=typeof e=="function"?e:null}function Of(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Tb({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||kb.has(r)||(kb.add(r),xb=xb.then(()=>eC({sessionName:e,from:t,to:r})).catch(()=>{}))}async function eC({sessionName:e,from:t,to:n}){await re({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await $f("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await JO.realpath(r),s=Of(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await $f("npm",["install","-g","@openai/codex"],{env:a,timeout:QO});let i=(await $f(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await re({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await vb({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await re({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await vb({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function vb(e){if(Rf)try{await Rf(e)}catch{}}var $f,QO,kb,xb,Rf,pl=j(()=>{dn();$f=ZO(XO),QO=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,kb=new Set,xb=Promise.resolve(),Rf=null});function nC(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
103
|
+
`);if(!Eb.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(tC);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"&&Eb.test(a)?a:"The agent is asking for approval";return{provider:t,box:n,promptId:`${t}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}function co(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
104
|
+
`);for(let r=n.length-1;r>=0;r--)if(Ib.test(n[r]))return n.slice(r).join(`
|
|
105
|
+
`);return t}function rC(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 oC(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 sC(e,t=""){let n=co(e);if(Ja.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
106
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Ab);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let p=r[d].trim();if(p){if(Ab.test(r[d])||/^Press enter/.test(p))break;f=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!Ib.test(r[l])&&rC(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=oC(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function cC(e,t=""){let n=co(e);if(Ja.test(n)||!aC.test(n))return null;let r=n.trimEnd().split(`
|
|
107
|
+
`).pop()||"";return iC.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 uC(e,t=""){let n=co(e);if(Ja.test(n)||!_b.test(n))return null;let r=n.split(`
|
|
108
|
+
`),o=r.findIndex(i=>_b.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(lC);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 dC(e,t=""){let n=co(e);return Ja.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 fC(e,t=""){let n=co(e);return Ja.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 pC(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Cf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
109
|
+
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function hC(e,t=""){let n=String(e);if(!mC.test(n)||Cf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
110
|
+
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function wC(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Cf.test(n)?null:{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:"cursor:signin-qr",questions:[{question:"Cursor is showing a QR sign-in code in the Terminal tab \u2014 scan it from another device to log in. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function $b(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=cC(t,n)||uC(t,n)||dC(t,n)||fC(t,n)||sC(t,n);if(r)return r}if(e==="cursor"){let r=hC(t,n)||wC(t,n)||pC(t,n);if(r)return r}return nC(t,{provider:e,box:n})}function Rb(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 tC,Eb,Ja,Ib,Ab,aC,iC,_b,lC,Cf,mC,ml=j(()=>{tC=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Eb=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;Ja=/(^|\n)\s*›/,Ib=/^\s*Welcome to Codex\b/;Ab=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;aC=/Successfully logged in\./,iC=/^\s*Press Enter to continue\.\s*$/;_b=/Enter this one-time code/,lC=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Cf=/Run Everything|Add a follow-up|Plan, search, build anything/;mC=/click this link to log in|Signing in with the browser/});function kC(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
111
|
+
`),n=-1;for(let s=t.length-1;s>=0;s--)if(t[s].trim()){n=s;break}if(n<0)return!1;let r=t[n];if(yC.test(r)||bC.test(r)){for(let s=n-1;s>=0&&n-s<=64;s--)if(/^\s*›/.test(t[s]))return!0}return/esc to interrupt|\bWorking \(/.test(String(e||""))}function EC(e){let t=String(e||"");if(!qe.codex.block.test(t))return null;let n=co(t);if(!qe.codex.block.test(n))return null;let r=n.match(TC);return{from:r?.[1]||null,to:r?.[2]||null}}function fn(e){return new Promise(t=>setTimeout(t,e))}function Mf(e,t){let n=Date.now(),r=AC();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 Nb(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
112
|
+
`),r=-1;for(let o=n.length-1;o>=0;o--)if(Mb.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
113
|
+
`)}function IC(e){let t=0;for(let n of String(e).split(`
|
|
114
|
+
`))Mb.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Nf(e,t){let n=qe[e];if(!n?.busy)return{state:"idle",reason:null};if(Df(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(Nb(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=IC(t);return e==="claude"&&r>0&&r<_C?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Pf(e,t){return Nf(e,t).state}function Pb(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
115
|
+
`).slice(-RC).join(`
|
|
116
|
+
`);return $C.test(n)}function ws(e,t){return Pf(e,t)==="busy"}function gl(e,t){return Pf(e,t)!=="idle"}function gs(e,t){let n=qe[e];return n?.up?n.up.test(t):!0}function OC(e,t){let n=qe[e];return n?Df(e,t)?!1:n.up.test(t)&&Pf(e,t)==="idle":!0}function Df(e,t){let n=qe[e];return n?n.auth?.test(t)&&!n.up.test(t)?!0:n.block?.test(t)?e==="codex"?!0:!n.up.test(t):!1:!1}function Bf(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
117
117
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
118
|
-
`);return e}function
|
|
119
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
120
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
121
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
122
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
123
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
124
|
-
`):""}async function
|
|
118
|
+
`);return e}function Db(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function Ob(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 Cb(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await oo(e).catch(()=>{});;){let l=Bf(await Ce(e),o);if(OC(t,l)){await s?.(),await fn(hs);return}if(qe[t]?.up&&!qe[t].up.test(l))if(await Mn(e)){if(c??=Date.now(),Date.now()-c>=Wa()){let u=Ob(t);throw re({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,idleMs:Date.now()-c,error:u.message}).catch(()=>{}),u}}else c=null;else c=null;if(r&&t==="claude"&&SC.test(l)&&!qe.claude.up.test(l)){re({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="codex"&&vC.test(l)&&!qe.codex.up.test(l)&&i<3){if(!Mf(e,"codex-trust")){await fn(hl);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await Ze(e,"",{enter:!0,kind:"codex-trust-accept"}),await fn(hs);continue}if(t==="cursor"&&xC.test(l)&&!qe.cursor.up.test(l)&&i<3){if(!Mf(e,"cursor-trust")){await fn(hl);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await Ze(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await fn(hs);continue}if(t==="codex"&&i<3){let u=EC(l);if(u){if(!Mf(e,`codex-update:${u.from}->${u.to}`)){await fn(hl);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await Ze(e,"3",{enter:!1,kind:"codex-update-skip"}),Tb({sessionName:e,from:u.from,to:u.to}),await fn(hs);continue}}if(Date.now()>a){if(Df(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=qe.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"&&qe.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"&&qe.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"&&qe.claude.block.test(l)&&(u='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),re({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await Mn(e)){let u=Ob(t);throw re({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u.message}).catch(()=>{}),u}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),re({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!qe[t]?.block?.test(l),paneUp:!!qe[t]?.up?.test(l),paneBusy:!!qe[t]?.busy?.test(Nb(qe[t],l))}).catch(()=>{}),await fn(hs);return}await fn(hl)}}function jf({agent:e,agentCmd:t}={}){let n=t?dr(t):dr(e||"");return{label:n,realClaude:t?ro(t):n==="claude",realCodex:t?/codex/.test(t)&&!/fake-codex|fake-lead/.test(t):n==="codex",realCursor:t?/cursor/.test(t)&&!/fake-cursor|fake-box/.test(t):n==="cursor"}}function Bb({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=jf({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function Xa({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}={}){let{realCodex:s,realCursor:a}=jf({agent:n,agentCmd:r});return!t||!s&&!a?null:hr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).catch(()=>{})}async function hr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=gC(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:f,realCursor:d}=jf({agent:r,agentCmd:o});if(u&&e&&await or(e),f&&e&&await Jo(e),d&&e&&await Xo(e),(u||f||d)&&n){await Cb(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});return}let p=qe[r]?r:qe[l]?l:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&p){await Cb(n,p,s,{launchEcho:i});return}await fn(hs)}var gC,hl,hs,yC,bC,qe,xC,vC,SC,TC,AC,wl,_C,Mb,$C,RC,Za=j(()=>{lt();Ta();Dc();Bc();pl();ml();dn();gC=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,hl=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,hs=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,yC=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,bC=/^\s*tab to queue message\b/;qe={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:kC},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/}},xC=/Trust this workspace|\[a\]\s*Trust/,vC=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,SC=/Resume from summary/i,TC=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;AC=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,wl=new Map;_C=68,Mb=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;$C=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,RC=15});function PC(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
119
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=NC}function pn(e,t){return e==="codex"?{enter:!0,submitDelayMs:CC,...PC(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:MC}:{enter:!0}}var CC,MC,NC,Qa=j(()=>{CC=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),MC=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),NC=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function ei(e,t){if(e==="codex")return FC(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
120
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(jb.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(jb.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(DC);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 FC(e){let t=String(e).replace(/\r/g,"").split(`
|
|
121
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(BC.test(t[o])||jC.test(t[o]))&&!Lf.test(t[o])?n=o:Lf.test(t[o])&&/\[Pasted Content \d+ chars\]/.test(t[o])&&(n=o+1);break}if(n<=0)return null;let r=[];for(let o=n-1;o>=0&&n-o<=60;o--){let s=t[o],a=s.match(Lf);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(LC.test(s))return null;r.unshift(s)}return null}function Ff(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=We(n.replace(UC,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!We(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function We(e){return String(e||"").replace(/\s+/g," ").trim()}function Lb(e,t,{slackMs:n=2e4}={}){let r=We(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&&We(a.text).includes(r));return s>=0&&s<o.length-1}function Fb(e,t,n,{busy:r=!1}={}){let o=ei(e,n);return o&&yl(t,o)||o&&e==="codex"&&Ff(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function yl(e,t){let n=We(e),r=We(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var jb,DC,Lf,BC,jC,LC,UC,bl=j(()=>{jb=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,DC=/^❯[ ]?(.*)$/,Lf=/^\s*›[ ]?(.*)$/,BC=/\S+ · \S/,jC=/^\s*(?:\? for shortcuts|tab to queue message)\b/,LC=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;UC=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function ni({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say"},a={}){if(!HC.has(t))return!0;let i=a.waitForUp??Bb({agent:t,agentCmd:n}),c=a.capture||(R=>Ce(R,{lines:Xe})),l=a.pressEnter||(R=>Le(R,["Enter"],{kind:"submit-retry"})),u=a.sleep||GC,f=a.now||Date.now,d=a.log||(R=>re(R).catch(()=>{})),p=a.maxMs??KC(),h=f()+p,w={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},g=0,k=0,S=0,E=!1;for(await u(ti[0]);;){let R=await c(e).catch(()=>""),B=gs(t,R),T=ws(t,R),_=Fb(t,r,R,{busy:T});if(_==="submitted")return!0;if(i&&(!B||_==="stuck"&&T)){if(f()>=h)return _==="stuck"?(d({...w,phase:"submit-failed",reason:"stuck-at-deadline",retries:g}),!1):(d({...w,phase:"submit-unverified",reason:E?"cli-down":"cli-never-up",retries:g,waitedMs:p}),!0);await u(WC);continue}if(E=!0,_==="stuck"){if(g>=qC)return d({...w,phase:"submit-failed",retries:g}),!1;g+=1,k=0,S=0,await l(e),d({...w,phase:"submit-retry",attempt:g}),await u(ti[0]);continue}if(k+=1,k>=ti.length||f()>=h)return!0;S+=1,await u(ti[Math.min(S,ti.length-1)])}}var HC,ti,WC,qC,KC,GC,Uf=j(()=>{lt();Za();bl();dn();HC=new Set(["claude","codex"]),ti=[250,450,700],WC=500,qC=2,KC=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},GC=e=>new Promise(t=>setTimeout(t,e))});function Pn(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of VC){let o=0;for(let s of zC[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Ub(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))YC.has(r)&&(t+=o||0),JC.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function ri(e){return String(e||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var VC,zC,YC,JC,kl=j(()=>{VC=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],zC={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope out\b/,/\btalk through\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/]};YC=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),JC=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Kb(e){let t=ri(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||XC.has(r)||n.push(r);return n}function ZC(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(qb(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(qb(n,"assistant",o))}}return t}function qb(e,t,n){let r=Pn(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 oi(e){let t=ZC(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Kb(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function xl(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(Kb(t))];if(!i.length)return[];let c=new Map;for(let u of i){let f=e.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(e.N-d+.5)/(d+.5));for(let[h,w]of f){let g=e.lengths[h],k=w+Hb*(1-Wb+Wb*(g/(e.avgdl||1))),S=p*(w*(Hb+1)/k);c.set(h,(c.get(h)||0)+S)}}let l=[];for(let[u,f]of c){let d=e.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var Hb,Wb,XC,Hf=j(()=>{kl();Hb=1.5,Wb=.75,XC=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 Gb from"node:path";async function QC(e){let t=Gb.join(e,".bostrat","events"),n;try{n=await vl.readdir(t)}catch{return oi([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await vl.readFile(Gb.join(t,o),"utf8");for(let a of s.split(`
|
|
122
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return oi(r)}function eM(e){let t=String(e||""),n=a=>{let i=t.match(a);return i?i[1].trim():""},r=n(/#+\s*Objective\s*\n([\s\S]*?)(?:\n#+\s|$)/i),o=n(/#+\s*Next steps?[^\n]*\n([\s\S]*?)(?:\n#+\s|$)/i);return`${r}
|
|
123
|
+
${o}`.trim()||t.slice(0,2e3)}function tM(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=xl(e,t,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${Vb}, not a summary)`,"_Auto-selected from the Track's event log by lexical relevance to the Objective / Next steps. Verbatim excerpts, not re-summarization (docs/memory-segmentation-indexing-scope.md \xA71.3)._","",...a,""].join(`
|
|
124
|
+
`):""}async function zb(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(Vb))return{appended:!1,reason:"already-enriched"};let i=await QC(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=tM(i,eM(a),{k:r,maxChars:o,exclude:s});return c?(await vl.appendFile(t,`
|
|
125
125
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
126
|
-
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var
|
|
127
|
-
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function
|
|
126
|
+
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var Vb,Yb=j(()=>{Hf();Vb="Relevant prior discussion (verbatim, retrieved"});function iM(e){return nM.test(String(e||"").trimStart())}function Zb(e){let t=String(e||"");if(!iM(t.trimStart()))return null;let n=t.replace(rM,"").trim(),r=cM(n),o=[];for(let u of sM){let f=u.from.map(d=>ys(r.get(d))).find(d=>d&&!Jb(d));f&&o.push({h:u.h,text:si(f,oM)})}let s=ys(r.get("Current Work"))||ys(r.get("Primary Request and Intent"))||uM(n),a=Tl(s),i=si(Sl(Kf(a[0]||"")),aM)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(si(Sl(Kf(l)),qf));for(let u of["Pending Tasks","Optional Next Step"]){let f=Tl(ys(r.get(u)))[0]||"";if(!(!f||Jb(f))){c.push(si(`Left to do: ${Sl(Kf(f))}`,qf));break}}if(!c.length){let u=Tl(ys(r.get("Problem Solving"))||"")[0];u&&c.push(si(Sl(u),qf))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function cM(e){let t=new Map,n=[];Wf.lastIndex=0;for(let r=Wf.exec(e);r;r=Wf.exec(e))n.push({name:lM(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 lM(e){return Xb.find(n=>n.toLowerCase()===e.toLowerCase())||e}function ys(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
127
|
+
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function uM(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return ys(t.split(/\n{2,}/).slice(0,2).join(" "))}function Tl(e){let t=String(e||"").trim();if(!t)return[];let r=t.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function Kf(e){return String(e||"").replace(/^(?:Immediately before (?:this|the) summary(?: request)?[,:]?\s*|At the (?:time|point) of (?:this )?compaction[,:]?\s*|Currently[,:]?\s*|The (?:most recent|immediately preceding|current) work (?:was|is)\s*)/i,"").replace(/^(?:I(?:'m|'d| am| was| had| have)?\s+(?:currently\s+|just\s+)?(?:been\s+)?(?:working on|in the middle of|completing|finishing)\s+|The user(?:'s)?\s+(?:most recent\s+)?(?:request|ask) (?:was|is)\s+(?:to\s+)?)/i,"").replace(/^I(?:'m|'d| am| was| had| have)?\s+(?:just\s+|then\s+)?(?=(?:[a-z]+(?:ed|ing)|ran|wrote|built|made|began|found|left|sent|took|went|did|got|kept|held|brought|caught|chose|came|saw|said|gave|told|set|put|read)\b)/,"").replace(/^["“]|["”]$/g,"").replace(/^(\w)/,t=>t.toUpperCase()).trim()}function Sl(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Jb(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=Tl(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 nM,Xb,Wf,rM,oM,sM,aM,qf,Qb=j(()=>{nM=/^This session is being continued from a previous conversation that ran out of context\./,Xb=["Primary Request and Intent","Key Technical Concepts","Files and Code Sections","Errors and Fixes","Problem Solving","All user messages","Pending Tasks","Current Work","Optional Next Step"],Wf=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Xb.join("|")})[:*\t ]*$`,"gim"),rM=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,oM=420,sM=[{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"]}],aM=116,qf=168});function tk(e){return Gf.has(String(e||"").replace(/^\//,"").toLowerCase())}function El(e){let t=String(e||"").trim();if(!t)return null;let n=Zb(t);if(n)return n;let r=t.match(dM);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(fM);if(o)return{kind:"skill",name:o[1]};let s=t.match(pM);if(s){let i=s[1].toLowerCase();return Gf.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(SM);if(a&&Gf.has(a[1].toLowerCase()))return{kind:"suppress"};if(mM.test(t))return{kind:"suppress"};if(wM.test(t))return{kind:"suppress"};if(hM.test(t))return{kind:"suppress"};if(gM.test(t)){let i=l=>TM(t.match(l)?.[1].trim()||"")||null,c=i(vM);return{kind:"task",summary:i(yM)||"",taskId:i(bM),toolUseId:i(kM),status:i(xM)?.toLowerCase()||null,resultHead:c?c.length>ek?`${c.slice(0,ek).trimEnd()}\u2026`:c:null}}return null}function TM(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function nk(e){return e?.kind==="skill"?`Ran the ${e.name} skill`:e?.kind==="command"?`Ran ${e.command}`:e?.kind==="task"?e.summary||"A background task finished":e?.kind==="resume-summary"?e.title?`Context resumed from summary: ${e.title}`:"Context resumed from summary":""}function rk(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var dM,fM,pM,mM,hM,wM,gM,yM,bM,kM,xM,vM,ek,Gf,SM,Vf=j(()=>{Qb();dM=/^Base directory for this skill:\s*(\S+)/,fM=/^Run the "([^"]+)" workflow\./,pM=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,mM=/^<local-command-(?:stdout|caveat)>/i,hM=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,wM=/^Continue from where you left off\.$/,gM=/^<task-notification>/i,yM=/<summary>([\s\S]*?)<\/summary>/i,bM=/<task-id>([\s\S]*?)<\/task-id>/i,kM=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,xM=/<status>([\s\S]*?)<\/status>/i,vM=/<result>([\s\S]*?)(?:<\/result>|$)/i,ek=600,Gf=new Set(["model","clear","compact","login","exit","quit"]);SM=/^\/([a-z0-9:_-]+)$/i});import EM from"node:crypto";function $M(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,sk)){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,AM)){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<_M)return null;n.push({question:o,header:ai(r?.header).slice(0,IM),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function zf(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;ok.lastIndex=0;let t=null;for(let o;o=ok.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=$M(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Yf(e,t,n){let r=EM.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Jf(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=sk}={}){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||zf(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Yf(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 ak(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
128
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
129
129
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
130
|
-
${o}`),s||""}var
|
|
130
|
+
${o}`),s||""}var sk,AM,_M,IM,ok,ai,ik,Xf=j(()=>{sk=4,AM=4,_M=2,IM=24,ok=/^[ \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():"";ik=`
|
|
131
131
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
132
132
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
133
133
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -147,72 +147,76 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
147
147
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
148
148
|
anything you can decide yourself; keep asking in prose for questions without
|
|
149
149
|
enumerable options.
|
|
150
|
-
`});function QC(e){return{id:e.id,kind:"prompt",groupId:e.groupId,provider:e.provider,box:e.box,agent:e.provider,questionTotal:e.questions.length,answeredVia:"chat",questions:e.questions.map((t,n)=>({index:n,header:t.header||"",question:t.question,multiSelect:!!t.multiSelect,chosen:[]})),ts:e.ts}}function tM(e,t,n){let r=Gb(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function dr(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,d=(b,h)=>{u.set(b,h),t.push(h)},f=b=>{let h=u.get(b);h&&(h.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let h=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let g of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let _=g[0].split("/").pop().toLowerCase();m.has(_)||m.set(_,[]),m.get(_).push(b.payload?.ts??b.ts)}}let w=3*6e4,y=20*6e4,x=(b,h)=>!!(b?.name&&m.get(String(b.name).toLowerCase()))?.some(_=>_>=h-w&&_<=h+y);for(let b of e||[])if(b.type==="turn.retracted"){r.add(`${b.boxId}|${b.payload?.saidTs}`);let h=Fe(b.payload?.text);h&&(o.has(b.boxId)||o.set(b.boxId,[]),o.get(b.boxId).push(h))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let h=Fe(b.payload?.text);h&&(s.has(b.boxId)||s.set(b.boxId,new Set),s.get(b.boxId).add(h))}else if(b.type==="job.started"&&b.payload?.id)a.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&c.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let h=b.payload.note,g={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:me(b)};h.toolUseId&&i.set(h.toolUseId,g);let _=String(h.summary||"").match(p);_&&l.set(_[1],g)}let T=new Map,I=new Map,$=null,D=new Map,S=new Map,A=new Map,R=new Map,E=new Map,L=new Map,U=new Map,se=new Map,G=new Map,C=new Map,Z=new Map,Y=new Map,te=new Map,we=new Map,ce=new Map,V=new Map,ge=()=>{for(let b of V.values())b.questions.length&&t.push(b);V.clear()},B=null,ye=6e4,be=new Map,_e=(b,h)=>{let g=o.get(b);if(!g?.length)return!1;let _=Fe(h),ne=s.get(b);return g.some(ae=>{if(!_.startsWith(ae))return!1;let Te=Fe(_.slice(ae.length));return!!Te&&!!ne?.has(Te)})};for(let b of e||[]){let h=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"||!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let g=h.note||gl(h.text);if(g){if(g.kind==="task"){if(g.toolUseId&&a.has(g.toolUseId))break;let _=String(g.summary||"").match(p);if(!g.toolUseId&&_&&c.has(_[1]))break}if(g.kind==="command"&&Lb(g.command))break;if(g.kind==="resume-summary"){t.push({id:b.id,kind:"resume-summary",title:g.title||"Context resumed from summary",bullets:g.bullets||[],sections:g.sections||[],rawChars:g.rawChars??null,ts:me(b),box:b.boxId,agent:b.provider});break}g.kind!=="suppress"&&t.push({id:b.id,kind:"event",type:Ub(g),name:g.name??null,command:g.command??null,summary:g.summary??null,status:g.status??null,ts:me(b),box:b.boxId,agent:b.provider});break}}if(r.has(`${b.boxId}|${me(b)}`)||b.source==="transcript"&&_e(b.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:b.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:me(b),box:b.boxId,agent:b.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(B=null);break}for(let g of D.values())g.status="stale";D.clear();for(let g of A.values())g.status="stale";A.clear(),B&&B.box===b.boxId&&(t.push(QC(B)),B=null),t.push({id:b.id,kind:"message",role:"user",text:h.text,ts:me(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,...b.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let g=h.from||{};t.push({id:b.id,kind:"event",type:"peer-message",text:h.text,from:g.trackName||g.boxId||g.name||null,fromTrackId:g.trackId||null,ts:me(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let g=tb(h,{codexFailedText:Qr});if(g){t.push({id:b.id,kind:"event",type:"provider-error",cls:g.cls,status:g.status??void 0,text:g.text,provider:b.provider,legacy:!0,at:me(b),ts:me(b),box:b.boxId,agent:b.provider});break}ge();let _=(h.images||[]).filter(Ee=>!x(Ee,me(b)));if(!String(h.text||"").trim()&&!_.length)break;if(b.provider==="cursor"){let Ee=`${b.boxId}|${h.text}`;if(n.has(Ee))break;n.add(Ee)}(!B||B.box===b.boxId)&&(B=null);let ne=b.provider==="codex"?(h.tools||[]).filter(Ee=>!wf.has(Ee)&&!Ha.has(Ee)):h.tools||[],ae=h.text,Te=null;if(jf.has(b.provider)){let Ee=Lf(String(h.text||""));Ee&&(ae=Ee.text,Te={questions:Ee.questions})}t.push({id:b.id,kind:"message",role:"assistant",text:ae,ts:me(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,...Te?{ask:Te}:{},...ne.length?{tools:ne}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},..._.length?{images:_}:{}}),Te&&(B={id:b.id,groupId:Ff(b.provider,h.sourceId||b.id,Te.questions),provider:b.provider,box:b.boxId,questions:Te.questions,ts:me(b)});break}case"job.started":{if(!h.id)break;let g=me(b),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let Le=l.get(h.name);Le&&Le.ts>=g&&(_=Le)}let ne={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:_?_.status==="killed"?"stopped":_.status&&_.status!=="completed"?"failed":"done":"running",startedTs:g,finishedTs:_?.ts??null,summary:_?.summary??null,resultHead:_?.resultHead??null},ae=be.get(b.boxId),Te=ae?.jobs[ae.jobs.length-1]?.startedTs;if(ae&&Te!=null&&g-Te<=ye)ae.jobs.push(ne);else{let Le={id:b.id,kind:"job-dock",dockId:h.id,ts:g,box:b.boxId,agent:b.provider,jobs:[ne]};be.set(b.boxId,Le),t.push(Le)}break}case"handoff.done":t.push(Rt(b,"lead-handoff"));break;case"subtask.delegated":t.push({...Rt(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":t.push(Rt(b,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let _ of D.values())_.status="stale";D.clear();for(let _ of A.values())_.status="stale";A.clear()}let g=Rt(b,`pr-${h.change}`);if(g.box=b.boxId??null,eM.has(h.change)&&h.number!=null){let _=tM(T,I,h);if(T.size>=2){for(let ae of I.values())for(let Te of ae)Te.grouped=!0;g.groupPrs=[...T.values()];let ne=[g];for(let ae of T.keys())I.set(ae,ne)}else{let ne=I.get(_)??[];ne.push(g),I.set(_,ne)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=Gb(h),ne=$&&!$.complete||T.size>=2;if(T.has(_))T.delete(_),I.delete(_);else if(h.number!=null)for(let ae of[...T.keys()])ae.endsWith(`#${h.number}`)&&(T.delete(ae),I.delete(ae));if(ne&&h.number!=null){(!$||$.complete)&&($={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:me(b),ts:me(b),box:b.boxId,agent:b.provider},t.push($)),$.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:me(b)}),$.remaining=[...T.values()],$.complete=T.size===0;break}}t.push(g);break}case"pr.offered":{if(!h.offerId)break;for(let _ of D.values())_.status="stale";D.clear();for(let _ of A.values())_.status="stale";A.clear();let g={id:b.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:me(b),box:b.boxId,agent:b.provider};D.set(h.offerId,g),S.set(h.offerId,g),t.push(g);break}case"pr.offer.answered":{let g=S.get(h.offerId);g&&(g.status=h.action||"dismiss",g.actions=Array.isArray(h.actions)?h.actions:null,g.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(g.existingPr={number:h.existingPr.number??null}),D.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let _ of A.values())_.status="stale";A.clear();let g={id:b.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:me(b),box:b.boxId,agent:b.provider};A.set(h.offerId,g),R.set(h.offerId,g),t.push(g);break}case"pr.exists.answered":{let g=R.get(h.offerId);g&&(g.status=h.choice||"dismiss",A.delete(h.offerId));break}case"box.state.changed":{let g=h.change==="model"?"model-change":h.change;if(!g)break;let _=Rt(b,g);if(g==="model-change"&&(_.to=h.model),g==="node-update"&&_.latest){let ne=t.findIndex(ae=>ae.kind==="event"&&ae.type==="node-update"&&ae.latest===_.latest);if(ne>=0){let ae=t[ne];t[ne]={..._,ts:ae.ts,at:ae.at,firstAt:ae.firstAt??ae.ts,updatedAt:_.ts};break}}t.push(_);break}case"track.transferred":case"track.recovered":t.push(Rt(b,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){f(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;d(`view:${h.viewId}`,{id:b.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:me(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{t.push({id:b.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:me(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:b.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:me(b),box:b.boxId,agent:b.provider});break}if(h.change==="deleted"){f(`mock:${h.mockId}`);break}if(h.change!=="created")break;d(`mock:${h.mockId}`,{id:b.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:me(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(h.change==="deleted"){f(`flow:${h.flowId}`);break}if(h.change!=="created")break;d(`flow:${h.flowId}`,{id:b.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:me(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){f(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;d(`runbook:${h.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:me(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{t.push({id:b.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:me(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let _ of U.values())_.box===(h.box||b.boxId)&&(_.status="stale",U.delete(_.offerId));let g={id:b.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:me(b),box:h.box||b.boxId,agent:h.agent||b.provider};U.set(h.offerId,g),se.set(h.offerId,g),t.push(g);break}case"box.fullscreen.answered":{let g=se.get(h.offerId);g&&(g.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",U.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let _ of E.values())_.key===h.key&&(_.status="stale",E.delete(_.offerId));let g={id:b.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:me(b),box:b.boxId,agent:b.provider};E.set(h.offerId,g),L.set(h.offerId,g),t.push(g);break}case"ticket.move.answered":{let g=L.get(h.offerId);g&&(g.status=h.action==="moved"?"moved":"dismiss",E.delete(h.offerId));break}case"ticket.state.changed":{for(let g of E.values())g.key===h.key&&g.to===h.to&&(g.status="stale",E.delete(g.offerId));if(h.mode==="offer")break;t.push(Rt(b,"ticket-state"));break}case"ticket.commented":{t.push({id:b.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:me(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":t.push(Rt(b,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let _ of G.values())_.status="stale";G.clear();let g={id:b.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:me(b),box:b.boxId,agent:b.provider};G.set(h.proposalId,g),C.set(h.proposalId,g),t.push(g);break}case"ticket.create.answered":{let g=C.get(h.proposalId);g&&(g.status=h.action==="created"?"created":"dismiss",g.createdCount=h.createdCount??null,g.failedCount=h.failedCount??null,G.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let _ of Z.values())_.status="stale";Z.clear();let g={id:b.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,nodeId:h.nodeId??null,nodeName:h.nodeName??null,status:"open",ts:me(b),box:b.boxId,agent:b.provider};Z.set(h.proposalId,g),Y.set(h.proposalId,g),t.push(g);break}case"loop.create.answered":{let g=Y.get(h.proposalId);g&&(g.status=h.action==="created"?"created":"dismiss",Z.delete(h.proposalId));break}case"loop.created":{let g=h.proposalId?Y.get(h.proposalId):null;g&&(g.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let ne of te.values())ne.loopId===h.loopId&&(ne.status="stale",ne.staleReason="superseded",te.delete(ne.proposalId));let g=b.type==="loop.update.offered",_={id:b.id,kind:g?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...g?{patch:h.patch??{},changed:Array.isArray(h.changed)?h.changed:Object.keys(h.patch||{})}:{reason:h.reason??""},selfEdit:!!h.selfEdit,status:"open",staleReason:null,ts:me(b),box:b.boxId,agent:b.provider};te.set(h.proposalId,_),we.set(h.proposalId,_),t.push(_);break}case"loop.update.answered":{let g=we.get(h.proposalId);g&&(h.action==="moved"?(g.status="stale",g.staleReason="moved"):g.status=h.action==="applied"?"applied":"dismiss",te.delete(h.proposalId));break}case"loop.delete.answered":{let g=we.get(h.proposalId);g&&(h.action==="moved"?(g.status="stale",g.staleReason="moved"):g.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",te.delete(h.proposalId));break}case"loop.updated":{let g=h.proposalId?we.get(h.proposalId):null;g&&(g.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"loop.deleted":case"loop.paused":break;case"rework.cause.offered":{if(!h.sha||ce.has(h.sha))break;let g={id:b.id,kind:"rework-cause-offer",sha:h.sha,subject:h.subject??"",linesReworked:Number(h.linesReworked)||0,origins:Array.isArray(h.origins)?h.origins:[],heuristic:h.heuristic??null,status:"open",cause:null,ts:me(b),box:b.boxId,agent:b.provider};ce.set(h.sha,g),t.push(g);break}case"rework.cause.answered":{let g=h.sha?ce.get(h.sha):null;g&&(g.status="answered",g.cause=h.cause??null);break}case"ticket.created":{let g={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},_=h.proposalId?C.get(h.proposalId):null;if(_){_.created=g;break}t.push({id:b.id,kind:"ticket-created",...g,ts:me(b),box:b.boxId,agent:b.provider});break}case"error":{if(!h.kind)break;t.push(Rt(b,h.kind));break}case"provider.error":t.push({...Rt(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":t.push(Rt(b,"usage-cap-hit"));break;case"usage.cap.warning":t.push(Rt(b,"usage-cap-warning"));break;case"provider.connected":{t.push({id:b.id,kind:"event",type:"provider-connected",provider:h.provider||b.provider,ts:me(b),at:me(b),box:b.boxId,agent:b.provider});break}case"prompt.answered":{if(B=null,h.control||!h.question)break;let g=h.groupId||h.promptId;if(!g)break;let _=V.get(g);_||(_={id:b.id,kind:"prompt",groupId:g,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:h.questionTotal??1,questions:[],ts:me(b)},V.set(g,_));let ne=h.questionIndex??0,ae={index:ne,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},Te=_.questions.findIndex(Le=>Le.index===ne&&Fe(Le.question)===Fe(ae.question));Te>=0?_.questions[Te]=ae:_.questions.push(ae),_.questions.sort((Le,bt)=>Le.index-bt.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(t.push(_),V.delete(g));break}default:break}}let Ie=u.size?t.filter(b=>!b.retired):t;return rM(Ie.sort((b,h)=>(b.ts??0)-(h.ts??0)))}function rM(e){let t=new Map,n=[];for(let r of e){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=t.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=nM){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};t.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else t.delete(r.box),n.push(r)}return n}function Wf(e,t=null){let n=[];for(let o of e||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:oM(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(t??dr(e)).filter(o=>o.kind==="message");return Vy(n,r)}function oM(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==rs(e.command)?t:lr(e.command,e.description||"")}function Rt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:me(e),ts:me(e)}}function Vb(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+sM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&aM.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function zb(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!cM.has(r.type)||!r.box)continue;let o=(r.ts??0)+iM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Jb(e){return(e||[]).filter(t=>lM.has(t?.kind)&&t.status==="open")}var me,eM,Gb,nM,sM,aM,iM,cM,lM,qf=j(()=>{os();eo();rl();dl();Bf();Hf();me=e=>e.payload?.ts??e.ts;eM=new Set(["opened","ci-fixed","reconciled","pushed"]),Gb=e=>`${e.repo??""}#${e.number}`;nM=2*6e4;sM=12e4,aM=new Set(["archived","unarchived","opened"]);iM=2500,cM=new Set(["pr-opened","pr-merged","pr-auto-armed"]);lM=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","fullscreen-offer"])});function mM(e){let t=null,n=null,r=null;for(let o of e??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(t={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:t,lastPush:n,lastEventAt:r}}function wM(e){for(let t=(e?.length??0)-1;t>=0;t--){let n=e[t];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function gM(e,t){let n=[],r=[],o=new Set,s=new Set;for(let a=(e?.length??0)-1;a>=0;a--){let i=e[a];if(i?.type!=="tool.called")continue;if(t!=null&&(i.ts??0)<=t)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<fM&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<pM&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Qb(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=mM(e),s=wM(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=dr(e??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-uM);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Zb[r]??Zb.failover),l.push(""),o.lastPush?.sha){let f=Xb(n,o.lastPush.ts),p=f?` (~${f} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let f=Xb(n,i);l.push(`## Last real checkpoint (written by the previous agent${f?`, ~${f} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=gM(e??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(f=>`\`${f}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(f=>`\`${f}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let f of c)l.push(`**${f.role==="user"?"operator":"agent"}:** ${hM(f.text,dM)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let d=l.join(`
|
|
151
|
-
`);return
|
|
152
|
-
|
|
153
|
-
[\u2026 brief truncated]`}var
|
|
154
|
-
|
|
155
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function _M(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(rk.test(s)){if(r.length>=mk)continue;let a=EM(s.replace(rk,"").replace(/[.;,:]+$/,""),hk,TM);a&&r.push(a)}else r.length||n.push(s);let o=n.join(" ").replace(/\s+/g," ").replace(/[:\-–—]+$/,"").trim();if(!o)throw new Error("beat file has no summary line");return o.length>nk&&(o=`${o.slice(0,nk-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function wk(e,t,{timeoutMs:n=9e4}={}){let r=pr.join(t,vM),o=pr.join(r,SM);await fr.mkdir(r,{recursive:!0});let s=await yl(o),a=await fe(e);if(!a)throw new Error(`session "${e}" not found`);return await bl(e,a,AM(o)),await Sk(o,s,n),_M(await fr.readFile(o,"utf8"))}function gk(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===uk||n===pk;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(lk)||n===bM||r.startsWith(dk)||r.startsWith(fk)}function yk(e){let t=[];for(let r of e?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-IM;t.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&t.push({box:r.to,from:s,to:o})}let n=e?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&t.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&t.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),t}function bk(e,t){let n=[];for(let r of e?.boxes||[]){let o=t?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function kk(e,{windows:t=[]}={}){if(gk(e.role,e.text))return!0;if(e.role!=="assistant"||!e.ts)return!1;for(let n of t)if(e.box===n.box&&e.ts>=n.from&&e.ts<=n.to)return!0;return!1}function kl(e){return Kf.get(e)||null}async function xk(e){let t=Kf.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await fe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:pr.join(n.cwd,ak,ik)};return Kf.set(e,r),OM(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function OM(e,t){e.step="summarize",await vk(e.name,t),e.step="clear";let n=await fe(e.name);await bl(e.name,n,"/clear"),await zf($M),e.step="resume",await bl(e.name,n,xM),e.step="done",e.finishedAt=Date.now()}async function vk(e,t,{timeoutMs:n=RM}={}){let r=pr.join(t,ak),o=pr.join(r,ik);await fr.mkdir(r,{recursive:!0});let s=await yl(o),a=await fe(e);await ee({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await bl(e,a,kM(o)),await Sk(o,s,n),await ee({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await yl(o)});let i=await Cb(t,o,{exclude:u=>gk(u.role,u.text)});await ee({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await O({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await fr.readFile(o,"utf8");await O({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...ck(u),ts:Date.now()}})}catch{}}await Wd(t);let l=await CM(r,o);return c&&await hy(c,l||o,{box:e,agent:a?.agent??null}),o}async function Gf(e,{timeoutMs:t}={}){let n=await fe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return vk(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Vf(e,t,{trackName:n="this Track"}={}){let r=await F({id:e,cwd:t}),o=Qb(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=pr.join(t,ok);return await fr.mkdir(pr.dirname(s),{recursive:!0}),await fr.writeFile(s,o,"utf8"),await O({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...ck(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),ok}async function yl(e){try{return(await fr.stat(e)).mtimeMs}catch{return 0}}async function Sk(e,t,n){let r=Date.now()+n;for(;;){if(await yl(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await zf(2e3)}}async function CM(e,t){try{if(!yM.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=pr.join(e,`${n}.md`);return await fr.copyFile(t,r),r}catch{return null}}function zf(e){return new Promise(t=>setTimeout(t,e))}function sk(e){return e==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":""}async function bl(e,t,n){if(t){let o=sk(t.agent);await ur({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await fe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await De(e,["Escape"],{kind:"drive-wipe"}),await zf(150),await Qo(e,el,{kind:"drive-wipe"}),await Je(e,n,{kind:"orchestration-drive",...sn(r.agent,n)}),await Ya({sessionName:e,agent:r.agent,agentCmd:sk(r.agent),text:n,flow:"orchestration-drive"})}var ak,ik,tk,lk,uk,dk,bM,kM,xM,fk,pk,vM,SM,nk,mk,hk,TM,AM,rk,EM,IM,$M,RM,Kf,ok,Jf=j(()=>{nt();Ga();rn();Va();Of();Mb();Aa();ue();Kc();ek();ak=".bostrat/handoff",ik="latest.md",tk=32*1024;lk="Checkpoint: write a work-in-progress handoff",uk="HANDOFF WRITTEN",dk="Resume from .bostrat/handoff/latest.md",bM="/clear",kM=e=>`${lk} to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${uk} and take no further action.`,xM=`${dk}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,fk="Beat: write a",pk="BEAT WRITTEN",vM=".bostrat/arc",SM="beat.txt",nk=200,mk=4,hk=5,TM=36,AM=e=>`${fk} short summary of what this workstream accomplished, to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${mk} further lines, each starting with "- " and naming ONE piece in at most ${hk} words \u2014 a label, not a sentence (e.g. "- stale working flag on poll"). Write nothing else in the file. When the file is written, reply only with ${pk} and take no further action.`,rk=/^(?:[-–—*•·]|\d+[.)])\s+/,EM=(e,t,n)=>{let r=e.split(/\s+/).filter(Boolean).slice(0,t).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};IM=6e5;$M=3500,RM=3e5,Kf=new Map;ok=".bostrat/handoff/status-latest.md"});function hr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!xl.test(String(e)))throw new Error(`pricing.js: ${n} window ${t} must be YYYY-MM-DD, got ${JSON.stringify(e)}`);let r=Date.parse(`${e}T${t==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${t} is not a real date: ${e}`);return r}function Ek(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:hr(o.from,"from",n),toMs:hr(o.to,"to",n)}))}return t}function Xf(e){if(e==null)return Date.now();if(typeof e=="number")return Number.isFinite(e)?e:null;if(e instanceof Date){let n=e.getTime();return Number.isFinite(n)?n:null}let t=Date.parse(String(e));return Number.isFinite(t)?t:null}function NM(e){if(!e)return null;let t=String(e).trim();return mr[t]?t:Tk[t]?Tk[t]:Object.keys(mr).find(r=>t.startsWith(r))||null}function PM(e){if(!e||e===mr)return MM;let t=Ak.get(e);return t||(t=Ek(e),Ak.set(e,t)),t}function _k(e,t,n={}){let{table:r,speed:o}=n,s=PM(r),a=r&&r!==mr?s[e]?e:null:NM(e);if(!a)return null;let i=Xf(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function an(e,t={},n,r){let o=_k(e,n,r);return o?((t.input||0)*o.input+(t.output||0)*o.output+(t.cacheWrite||0)*o.cacheWrite+(t.cacheRead||0)*o.cacheRead)/1e6:0}function DM(e=mr){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:hr(a.from,"from",n),toMs:hr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),xl.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`),a.fast&&(typeof a.fast[i]!="number"||!Number.isFinite(a.fast[i])||a.fast[i]<0)&&t.push(`${n}: fast variant ${i} must be a non-negative number, got ${JSON.stringify(a.fast?.[i])}`)}}return t}function jM({now:e=Date.now(),table:t=mr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...BM,...n},a=[],i=Xf(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(d=>({...d,fromMs:hr(d.from,"from",c),toMs:hr(d.to,"to",c),verifiedMs:xl.test(String(d.verifiedAt??""))?Date.parse(`${d.verifiedAt}T00:00:00.000Z`):NaN}))}catch(d){a.push(String(d.message));continue}for(let d of u){if(!Number.isFinite(d.verifiedMs)){a.push(`${c}: window ${d.from??"\u2026"} \u2192 ${d.to??"now"} has no verifiedAt`);continue}let f=i>=d.fromMs&&i<=d.toMs,p=d.fromMs>i&&d.fromMs-i<=o*Yf;f&&i-d.verifiedMs>r*Yf&&a.push(`${c}: current rate last verified ${d.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),p&&d.fromMs-d.verifiedMs>s*Yf&&a.push(`${c}: scheduled rate change on ${d.from} was last verified ${d.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function ls({now:e=Date.now(),table:t=mr,...n}={}){let r=Xf(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=hr(l.from,"from",i),d=hr(l.to,"to",i);r>=u&&r<=d&&xl.test(String(l.verifiedAt??""))&&(!o||l.verifiedAt<o)&&(o=l.verifiedAt),u>r&&s.push({model:i,from:l.from,verifiedAt:l.verifiedAt??null})}s.sort((i,c)=>i.from<c.from?-1:1);let a=[...DM(t),...jM({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var mr,Tk,xl,MM,Ak,Ik,BM,Yf,On=j(()=>{mr={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Tk={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},xl=/^\d{4}-\d{2}-\d{2}$/;MM=Ek(mr);Ak=new WeakMap;Ik=(e,t,n)=>_k(e,t,n)!==null;BM=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Yf=864e5});function LM(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Rk(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Ot(e){let t=e.payload?.model||"unknown",n=LM(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=an(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!Ik(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function wr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=$k();for(let f of n){let{usd:p,model:m,tokens:w,repriced:y,unpriced:x,asRecorded:T}=Ot(f);i+=T;let I=f.provider||"unknown",$=f.boxId||"unattributed";if(y&&c++,x&&(l++,u.add(m)),a+=p,Rk(d,w),r[m]||={usd:0,tokens:$k(),events:0},r[m].usd+=p,r[m].events+=1,Rk(r[m].tokens,w),o[I]||={usd:0,events:0},o[I].usd+=p,o[I].events+=1,s[$]||={usd:0,events:0,provider:I,models:new Set,label:t[$]||$},s[$].usd+=p,s[$].events+=1,m!=="unknown"&&s[$].models.add(m),f.epoch!=null){let D=(s[$].epochs||={})[f.epoch]||={usd:0,events:0,provider:I,models:new Set};D.usd+=p,D.events+=1,D.provider=I,m!=="unknown"&&D.models.add(m)}}return{totalUsd:us(a),asRecordedUsd:us(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:vl(r,f=>({...f,usd:us(f.usd)})),byProvider:vl(o,f=>({...f,usd:us(f.usd)})),byBox:vl(s,f=>({usd:us(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:vl(f.epochs,p=>({usd:us(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var $k,us,vl,gr=j(()=>{On();$k=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});us=e=>Math.round(e*1e4)/1e4,vl=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function FM(e){if(!Number.isFinite(e)||e<=0)return null;let t=e/864e5;if(t>=1)return`${Math.round(t)}d`;let n=e/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(e/6e4))}m`}function Ok(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let d=u.payload||{};!d.repo||d.number==null||(d.change==="merged"?o.set(`${d.repo}#${d.number}`,d):d.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(d=>{let[f,p]=d.split("#");return`${f.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Zf(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Zf(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(d=>`${d.fromAgent}\u2192${d.toAgent}`).join(", ");n.push(`${Zf(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=wr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?FM(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Zf,Ck=j(()=>{gr();Zf=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as UM}from"node:child_process";import{promisify as HM}from"node:util";import{randomUUID as WM}from"node:crypto";async function Qf(e,t){let n=Date.now()+t;for(;;){if(await In(e))return!0;if(Date.now()>n)return!1;await ti(KM)}}async function VM(e,t){let n=Nk[t]||Nk.default;n.escape&&(await De(e,["Escape"],{kind:"swap-exit"}),await ti(150)),n.wipe&&await Qo(e,el,{kind:"swap-exit"}),await tl(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=sn(t).submitDelayMs||0;await ti(Math.max(r,250)),await De(e,["Enter"],{kind:"swap-exit"})}async function Pk(e,t){try{let{stdout:n}=await no(W,q("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Dk(e){await no(W,q("respawn-pane","-k","-t",e))}async function ep(e,t={}){if(!Ne(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await fe(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||ar(n),a=Number(t.exitTimeoutMs)||qM;await ee({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await In(e)&&(await VM(e,o),!await Qf(e,a)&&(i=!0,await ee({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Dk(e),!await Qf(e,Mk))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Yr(n),l=c?Ec():null,u=await Pk(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await no(W,q("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await no(W,q("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(y){throw l?zc(y,l):y}if(d&&!i&&(await Dk(e),!await Qf(e,Mk)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,p=t.resumeSessionId||WM(),m=f?n:Pa(n,p,e),w=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Qn(r.cwd),s==="codex"&&await Fo(r.cwd),s==="cursor"&&await Uo(r.cwd),await Xr(e),await no(W,q("set-option","-t",e,"@bostrat-sid",p)),await no(W,q("set-option","-t",e,"@bostrat-agent",s)),await ti(GM),c&&Na().length&&(await Pk(e,Ma)||await no(W,q("set-environment","-t",e,Ma,"1")).catch(()=>{}),await Je(e,`export ${Ma}=1`,{kind:"swap-env"}),await ti(150)),await Je(e,m,{kind:"swap-launch"}),await ee({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:d,launchCmd:m}}var no,ti,qM,Mk,KM,GM,Nk,Bk=j(()=>{nt();ya();Ic();$c();Va();rn();no=HM(UM),ti=e=>new Promise(t=>setTimeout(t,e)),qM=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Mk=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,KM=250,GM=400,Nk={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}}});async function jk(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await gt(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
156
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let
|
|
157
|
-
`).trim():"",
|
|
158
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let
|
|
159
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
160
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
161
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
162
|
-
`),o=
|
|
163
|
-
`),o=
|
|
164
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
165
|
-
`)].reverse().find(a=>
|
|
166
|
-
`),o=
|
|
167
|
-
`),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match(
|
|
168
|
-
`),o
|
|
169
|
-
`).pop()||"")?!0:
|
|
170
|
-
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function
|
|
171
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
172
|
-
`).filter(Boolean);return t&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function
|
|
173
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
174
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
175
|
-
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
176
|
-
`),n=t.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
177
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function
|
|
178
|
-
`),n=t.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(t[n]),o=[];for(let s=n+1;s<t.length;s++){let a=t[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function H0(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim();if(e==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!L0.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=F0.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!VN.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ii(e,t,{timeoutMs:n=KN}={}){let r=Date.now()+n;for(;;){let o=await Oe(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Ml(GN)}}async function YN(e,t){return await Je(e,`/model ${t.model}`,sn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function XN(e,t){await Qc(e),await Je(e,"/model",{enter:!1}),await Ml(250),await De(e,["Enter"],{kind:"model-switch"});let n=await ii(e,P0);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await De(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=JN(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Je(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await ii(e,i=>D0(i)||!P0(i)&&o(i)&&{done:o(i)});if(!s)throw await De(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?L0.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await De(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Je(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ii(e,i=>!D0(i)&&o(i));if(!a)throw new Error("codex never confirmed the model change");return{verified:!0,model:a.model,effort:a.effort}}async function ZN(){let e=N0.join(process.env.CURSOR_CONFIG_DIR||N0.join(qN.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await WN.readFile(e,"utf8"))?.model||null}catch{return null}}async function QN(e,t){await Qc(e),await tl(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await ii(e,B0),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(t.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await ii(e,B0,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await De(e,["Down"],{kind:"model-switch"}),await Ml(250)}if(!n||!s||!o(s)){await De(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await De(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await De(e,["Enter"],{kind:"model-switch"}),await Ml(800);let a=await ZN();return{verified:!!a&&(a.modelId===t.model||a.displayModelId===t.model||r(a.displayName).startsWith(r(t.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function W0(e,t,n){return e==="codex"?XN(t,n):e==="cursor"?QN(t,n):YN(t,n)}var Ml,KN,GN,j0,VN,zN,L0,F0,q0=j(()=>{nt();Va();Ml=e=>new Promise(t=>setTimeout(t,e)),KN=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,GN=300,j0=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 5"},{alias:"sonnet",label:"Sonnet 5"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],VN=new Set([...j0.map(e=>e.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),zN=[{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.6-terra",label:"GPT-5.6 Terra"},{alias:"gpt-5.6-luna",label:"GPT-5.6 Luna"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}],L0=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),F0=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function Pl(e){let t=e||{},n=eP(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function eP(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:lt(e.agent),payload:{...kr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:lt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...kr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...kr(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...kr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...kr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...kr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{...kr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...kr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function V0(e,t){let n=Pl(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Cp(()=>O(r,n))}async function z0(e){if(!(!e?.id||K0.has(e.id))){K0.add(e.id);try{let t=await F(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=Pl(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await O(e,o)}}catch{}}}function kr(e){let{type:t,at:n,...r}=e;return r}function J0(e,t,n){!n?.promptId||Op.get(e.id)===n.promptId||(Op.set(e.id,n.promptId),Cp(()=>O(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:lt(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function Mp(e,t,n,r={}){Op.delete(e.id),Cp(()=>O(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,Nl,Cp,G0,K0,Op,Np=j(()=>{ue();lt=e=>e||null;Nl=Promise.resolve(),Cp=e=>(Nl=Nl.then(e).catch(()=>{}),Nl),G0=()=>Nl;K0=new Set;Op=new Map});function sP(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function tx(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of sP(n).reverse()){if(!tP.test(r))continue;let o=r.endsWith("?")&&r.length<=oP&&rP.test(r);if(nP.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function rx(e){let t=String(e||""),n=[];aP.test(t)&&n.push("commit"),nx.test(t)&&n.push("rebase"),iP.test(t)&&n.push("push");let r=lP.test(t);!r&&cP.test(t)&&n.push("pr");let o=null;if(r){let s=t.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||t.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function ox(e){return nx.test(String(e||""))}function vs(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let f=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(f+=` ${d.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(f+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),f}function sx(e,{branch:t=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(e)?e:["commit","push","pr"]),a=t?` on this branch (${t})`:"",i=[];s.has("commit")&&i.push(`commit the work${a} with a clear commit message`),s.has("rebase")&&i.push(`rebase ${s.has("commit")?"the branch":`this branch${t?` (${t})`:""}`} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`),i.push("move the work to a fresh branch (branch off the current HEAD with a clear new name)"),i.push("push the new branch to the remote"),i.push(`open a pull request${n?` against ${n}`:""} from the new branch with a concise title and a description of what changed and why (use \`gh pr create\`)`);let c=[];s.has("commit")||c.push("don't create any new commits \u2014 ship what's already committed"),o&&!s.has("rebase")&&c.push("don't rebase \u2014 leave the branch's history as it is");let l=`Yes \u2014 go ahead, as a NEW pull request: ${i.join(", then ")}.`;return c.length&&(l+=` ${c.join(", and ").replace(/^./,u=>u.toUpperCase())}.`),l+=` Leave the existing open PR${r?` #${r}`:""}${t?` and its remote branch (${t})`:" and its remote branch"} exactly as they are \u2014 don't push ${t||"the old branch"} itself.`,l}function ax(e,t=Date.now()){let n=[],r=null;for(let o of e||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&t-r.lastActivity<=uP&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function ix(e,t){let n=new Set;for(let r of t||[]){if(!r.settled)continue;let o=null;for(let s of e||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to+dP||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function cx(e){let t=(Array.isArray(e)?e:[]).filter(r=>Y0[r]).map(r=>Y0[r]);return`The ship you were given the go-ahead for${t.length?` (${t.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}var X0,Z0,Q0,ex,tP,nP,rP,oP,xs,aP,nx,iP,cP,lP,uP,dP,Y0,Ss=j(()=>{X0=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,Z0=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,Q0=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,ex=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|clean|interactive)\s)rebase(?!s\b)(?:\s+(?:it|this|that|them|(?:this|that|the)\s+branch))?(?:\s+(?:on(?:to)?|against|atop|over)\s+\S+)?/.source,tP=new RegExp(`\\b(?:${X0}|${Z0}|${Q0}|${ex})\\b`,"i"),nP=/\b(?:want\s+me\s+to|would\s+you\s+like(?:\s+me)?(?:\s+to)?|should\s+i|shall\s+i|do\s+you\s+want(?:\s+me)?(?:\s+to)?|happy\s+to|ready\s+for\s+me\s+to|let\s+me\s+know\s+(?:if|when|whether|and)|say\s+the\s+word|give\s+me\s+the\s+(?:word|go[\s-]?ahead)|if\s+you(?:'d|\s+would)?\s+(?:like|want|prefer)|just\s+say\s+(?:so|yes|go))\b/i,rP=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,oP=90;xs=["commit","rebase","push","pr"],aP=new RegExp(`\\b(?:${X0})\\b`,"i"),nx=new RegExp(`\\b(?:${ex})\\b`,"i"),iP=new RegExp(`\\b(?:${Z0})\\b`,"i"),cP=new RegExp(`\\b(?:${Q0})\\b`,"i"),lP=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;uP=3e4;dP=2500;Y0={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import fP from"node:crypto";import Dl from"node:fs/promises";import lx from"node:path";import pP from"node:os";async function Ue(){return xr||(Pp??=Dl.readFile(Bp,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(xr=e,!t&&Me()){let{records:n,source:r}=await Ye("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;xr[re(i.repo,i.number)]=i}Object.keys(xr).length&&await jp().catch(()=>{})}}return xr}).finally(()=>Pp=null),Pp)}async function jp(){let e=JSON.stringify({links:xr},null,2),t=`${Bp}.${process.pid}.tmp`;await Dl.mkdir(ux,{recursive:!0}),await Dl.writeFile(t,e),await Dl.rename(t,Bp)}async function Xe(e){Dp=Dp.then(jp,jp),await Dp.catch(()=>{});let t=xr?.[e];t&&ct("prlinks",{id:dx(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function St({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await Ue(),c=re(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:mP.has(a),firstSeen:Date.now()},await Xe(re(e,t)),{link:i[c],created:!0})}async function Nn(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||n.announced?null:(n.announced=!0,await Xe(re(e,t)),n)}async function Qe(e,t){return!e||t==null?null:(await Ue())[re(e,t)]??null}async function vr(e,t,n){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await Xe(re(e,t)),r):null}async function fx(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[re(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(re(e,t)),o):null}async function px(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ue())[re(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(re(e,t)),o):null}async function Bl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Xe(re(e,t)),r)}async function jl(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Xe(re(e,t)),n)}async function Ll(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Xe(re(e,t)),r)}async function Fl(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Xe(re(e,t)),n)}async function mx(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(r.baseFoldAt=n,await Xe(re(e,t)),r):null}async function hx(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Xe(re(e,t)),n)}async function wx(e,t,n){if(!e||t==null||!n)return null;let r=(await Ue())[re(e,t)];return r?(r.lastHeadSha=n,await Xe(re(e,t)),r):null}async function gx(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Ue())[re(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Xe(re(e,t))),{prev:o,link:r}}async function ci(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Xe(re(e,t)),r):null}async function fo(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Xe(re(e,t)),r):null}async function li(e,t,n=!0){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Xe(re(e,t)),r):null}async function yx(e,t,n){if(!e||t==null)return null;let r=(await Ue())[re(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Xe(re(e,t)),r}async function ui(e,t){if(!e||t==null)return null;let n=(await Ue())[re(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Xe(re(e,t)),n)}async function bx(){return Object.values(await Ue()).filter(e=>!hP.has(e.state))}async function po(){return Object.values(await Ue()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function kx(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ue(),r=0;for(let[o,s]of Object.entries(n))await ct("prlinks",{id:dx(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var ux,Bp,re,dx,xr,Pp,Dp,mP,hP,Pn=j(()=>{sr();ux=process.env.BOSTRAT_STATE_DIR||lx.join(process.env.HOME||pP.tmpdir(),".bostrat"),Bp=lx.join(ux,"pr-links.json"),re=(e,t)=>`${e}#${Number(t)}`,dx=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=fP.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},xr=null,Pp=null,Dp=Promise.resolve();mP=new Set(["api","observed","reconciled"]);hP=new Set(["merged","closed","lost"])});import wP from"node:fs";import Lp from"node:fs/promises";import Tx from"node:path";import gP from"node:os";function _x(e,t=!1){if(!t&&e-vx<yP)return;vx=e;let n=JSON.stringify({windowStart:Ts,byCaller:Object.fromEntries(Dn),lastHour:Sr});Sx=Sx.then(()=>Lp.mkdir(Ax,{recursive:!0})).then(()=>Lp.writeFile(`${Ul}.tmp`,n)).then(()=>Lp.rename(`${Ul}.tmp`,Ul)).catch(()=>{})}function Ix(e){e-Ts<Ex||(Sr={start:Ts,total:Hl(Dn),byCaller:Object.fromEntries(Dn)},Sr.total&&console.log(`[github] ${Sr.total} calls last hour`,Sr.byCaller),Ts=e,Dn=new Map,_x(e,!0))}function Bn(e="other",t=Date.now()){Ix(t),Dn.set(e,(Dn.get(e)||0)+1),_x(t)}function $x(e=Date.now()){return Ix(e),{windowStart:Ts,total:Hl(Dn),byCaller:Object.fromEntries(Dn),lastHour:Sr,backoffUntil:mo>e?mo:null}}function Tr(e){return bP.test(String(e?.message||e||""))}function Ar(e,t=Date.now()){return mo<=t&&console.warn(`[github] rate limited \u2014 backing off ${xx/6e4} min:`,String(e?.message||e||"").slice(0,200)),mo=Math.max(mo,t+xx),mo}function Wl(e=Date.now()){return Math.max(0,mo-e)}var Ax,Ul,Ex,xx,yP,Ts,Dn,Sr,mo,vx,Hl,Sx,bP,di=j(()=>{Ax=process.env.BOSTRAT_STATE_DIR||Tx.join(process.env.HOME||gP.tmpdir(),".bostrat"),Ul=Tx.join(Ax,"github-metrics.json"),Ex=60*6e4,xx=5*6e4,yP=6e4,Ts=Date.now(),Dn=new Map,Sr=null,mo=0,vx=0,Hl=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(wP.readFileSync(Ul,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Ex?(Ts=e.windowStart,Dn=new Map(Object.entries(e.byCaller||{})),Sr=e.lastHour||null):Hl(new Map(Object.entries(e.byCaller||{})))&&(Sr={start:e.windowStart,total:Hl(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Sx=Promise.resolve();bP=/rate limit|too quickly|abuse detection|HTTP 429/i});import Rx from"node:fs/promises";import Ox from"node:path";import kP from"node:os";function Kl({change:e,repo:t=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:e,repo:t,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};ql.push(l),ql.length>vP&&ql.shift();let u=Fp.get(e)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),Fp.set(e,u),SP(l),l}async function SP(e){try{await Rx.mkdir(Cx,{recursive:!0}),await Rx.appendFile(xP,JSON.stringify(e)+`
|
|
179
|
-
`)}catch{}}function Mx(){let e={};for(let[t,n]of Fp)e[t]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:e,recent:ql.slice(-20)}}var Cx,xP,vP,ql,Fp,Gl=j(()=>{Cx=process.env.BOSTRAT_STATE_DIR||Ox.join(process.env.HOME||kP.tmpdir(),".bostrat"),xP=Ox.join(Cx,"github-latency.ndjson"),vP=100,ql=[],Fp=new Map});import{execFile as TP}from"node:child_process";import{promisify as AP}from"node:util";async function EP(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Es("git",["-C",e,"rev-parse","HEAD"]),Es("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await O({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function Ct(e,t,n={}){let{caller:r="prs",...o}=n,s=await tn({cwd:e});Bn(r);try{let{stdout:a}=await Es("gh",t,{cwd:e,..._P,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Tr(a)&&Ar(a),a}}async function Wp(){if(Vl&&Date.now()-Vl.at<6e4)return Vl.data;let e;try{Bn("auth");let t=await Es("gh",["auth","status"],{timeout:1e4});e={installed:!0,authenticated:!0,login:`${t.stdout||""}${t.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(t){e=t?.code==="ENOENT"||t?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(e.installed&&!e.authenticated)try{let t=await xa();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Vl={at:Date.now(),data:e},e}async function IP(e){try{let{stdout:t}=await Es("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Px(e){let t=e.name||e.context||"check",n=e.detailsUrl||e.targetUrl||null,r=e.completedAt||e.createdAt||null,o;return e.__typename==="StatusContext"?o=e.state==="SUCCESS"?"pass":e.state==="PENDING"||e.state==="EXPECTED"?"pending":"fail":e.status&&e.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(e.conclusion)?o="pass":o="fail",{name:t,state:o,url:n,completedAt:r}}function Up(e){let t=(e||[]).map(Px),n=t.filter(s=>s.state==="pending").length,r=t.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=t.length&&!n&&r.length?Math.max(...r):null;return{total:t.length,passed:t.filter(s=>s.state==="pass").length,failed:t.filter(s=>s.state==="fail").length,pending:n,failing:t.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}function Kp(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function Bx(e,t){if(!t)return null;let n=await xe(e);if(!n)return null;try{let r=await Ct(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function jx(e){return(e||[]).map(t=>({name:t.name,detailsUrl:t.html_url||null,status:String(t.status||"").toUpperCase(),conclusion:t.conclusion?String(t.conclusion).toUpperCase():null,completedAt:t.status==="completed"&&t.updated_at||null}))}async function Lx(e,t){let n=await Bx(e,t);return n===null?{...Up([]),unavailable:!0}:Up(jx(n))}function zl(e,t=Up(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function RP(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){As.delete(s);continue}r.add(s),As.has(s)||As.set(s,n),o.mergeabilityPending=n-As.get(s)<$P}for(let o of As.keys())o.startsWith(`${e}#`)&&!r.has(o)&&As.delete(o);return t}async function _s(e,t){let n=await jn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Ct(n.path,["pr","view",String(r),"--json",qp]);return zl(JSON.parse(o))}catch(o){if(!Kp(o))throw o;let s=await Ct(n.path,["pr","view",String(r),"--json",Dx]),a=JSON.parse(s);return zl(a,await Lx(n.path,a.headRefOid))}}async function cn(e,{caller:t="prs"}={}){try{let n=await Ct(e,["pr","list","--json",qp,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>zl(r)).sort(Nx)}catch(n){if(!Kp(n))throw n;let r=await Ct(e,["pr","list","--json",Dx,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>zl(o,await Lx(e,o.headRefOid))))).sort(Nx)}}function un(){fi.clear()}async function MP(e,t){if(!e)return t;for(let n of t){let r=await Qe(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Bl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ll(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function Fx(){let e=await Wp();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await En())await IP(a.path)&&t.push(a);let n=Date.now(),r=Wl(n)>0,o=await Promise.all(t.map(async a=>{let i=fi.get(a.path),c=i?.pending?CP:OP;if(i&&(r||n-i.at<c))return i.entry;try{let l=await xe(a.path).catch(()=>null),u=RP(a.path,await cn(a.path,{caller:"tab"}),n);if(await MP(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Kl({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},f=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return fi.set(a.path,{at:Date.now(),entry:d,pending:f}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:ln(l)}}})),s=new Set(t.map(a=>a.path));for(let a of fi.keys())s.has(a)||fi.delete(a);return{auth:e,repos:o}}async function jn(e){let t=(await En()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ho(e){return Ho(e)}async function Is(e,t,{method:n="squash"}={}){let r=await jn(e);if(!pi.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(t);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await tn({cwd:r.path})).GH_TOKEN){let a=await Cd().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ct(r.path,s)}catch(a){throw new Error(ln(a))}finally{un()}return{ok:!0,number:o}}async function mi(e,t){let n=await jn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ct(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(ln(s))}finally{un()}let o=await xe(n.path).catch(()=>null);return o&&await mx(o,r).catch(()=>{}),{ok:!0,number:r}}async function Gp(e,t){let n=await jn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ct(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ln(o))}finally{un()}return{ok:!0,number:r}}function Vp(e){return e.green&&!e.isDraft}function BP(e){return e.isDraft?"draft":e.mergeable==="CONFLICTING"?`conflicts with ${e.baseRefName}`:e.checks.failed>0?"checks failing":e.checks.pending>0?"checks running":e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":e.mergeStateStatus==="BEHIND"?`behind ${e.baseRefName}`:e.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${e.mergeStateStatus})`}function Ux(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Vp(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Hx(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function hi(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function Wx(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(hi))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function qx(){let e={};for(let[t,n]of Hp)e[t]={...n};return e}async function Kx(e,{method:t="merge",onMerged:n=null}={}){let r=await jn(e);if(!pi.has(t))throw new Error(`invalid merge method: ${t}`);if(Hp.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await cn(r.path)).filter(Vp).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:t,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return Hp.set(r.path,s),LP(s,o,{listPrs:()=>cn(r.path),merge:a=>Is(r.path,a,{method:t}),sleep:DP,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function jP(e,t){let n=Date.now()+(t.settleMaxMs??PP),r=null;for(;;){let o=null;try{if(o=await t.listPrs(),r=null,!o.some(a=>e.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await t.sleep(NP)}}async function LP(e,t,n){try{let r=t;for(let o=0;o<t.length;o++){let s=t[o];if(e.current=s.number,o>0)try{r=await jP(t.slice(o).map(i=>i.number),n)}catch(i){for(let c of t.slice(o))e.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${ln(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){e.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!Vp(a)){e.skipped.push({number:a.number,title:a.title,reason:BP(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:ln(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),un()}}async function zp(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Es("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ln(l)}`)}await EP(e);let o=n||"";if((await tn({cwd:e})).GH_TOKEN){let l=await Cd().catch(()=>null);l&&(o=`${o?`${o}
|
|
180
|
-
|
|
181
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await
|
|
150
|
+
`});function RM(e){return{id:e.id,kind:"prompt",groupId:e.groupId,provider:e.provider,box:e.box,agent:e.provider,questionTotal:e.questions.length,answeredVia:"chat",questions:e.questions.map((t,n)=>({index:n,header:t.header||"",question:t.question,multiSelect:!!t.multiSelect,chosen:[]})),ts:e.ts}}function CM(e,t,n){let r=ck(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function wr(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,f=(y,m)=>{u.set(y,m),t.push(m)},d=y=>{let m=u.get(y);m&&(m.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,h=new Map;for(let y of e||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let m=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let b of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let M=b[0].split("/").pop().toLowerCase();h.has(M)||h.set(M,[]),h.get(M).push(y.payload?.ts??y.ts)}}let w=3*6e4,g=20*6e4,k=(y,m)=>!!(y?.name&&h.get(String(y.name).toLowerCase()))?.some(M=>M>=m-w&&M<=m+g);for(let y of e||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let m=We(y.payload?.text);m&&(o.has(y.boxId)||o.set(y.boxId,[]),o.get(y.boxId).push(m))}else if(y.type==="turn.user"&&y.source==="orchestrator"){let m=We(y.payload?.text);m&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(m))}else if(y.type==="job.started"&&y.payload?.id)a.add(y.payload.id),(y.payload.kind||"agent")==="agent"&&y.payload.name&&c.add(y.payload.name);else if(y.type==="turn.user"&&y.payload?.note?.kind==="task"){let m=y.payload.note,b={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:fe(y)};m.toolUseId&&i.set(m.toolUseId,b);let M=String(m.summary||"").match(p);M&&l.set(M[1],b)}let S=new Map,E=new Map,R=null,B=new Map,T=new Map,_=new Map,O=new Map,I=new Map,U=new Map,H=new Map,ae=new Map,X=new Map,C=new Map,Q=new Map,ve=new Map,W=new Map,be=new Map,se=new Map,pe=[],ie=y=>pe.push(y),L=y=>{if(!pe.length)return;let m=fe(y);for(let b of pe){if(b.status!=="open"||b.box&&b.box!==y.boxId||(b.ts??0)>m)continue;let M=t.indexOf(b);M>=0&&t.splice(M,1),b.ts=m,t.push(b)}},ce=new Map,ue=new Map,Ae=new Map,_e=new Map,xt=new Map,je=new Map,A=()=>{for(let y of je.values())y.questions.length&&t.push(y);je.clear()},G=null,Ge=6e4,_t=new Map,at=(y,m)=>{let b=o.get(y);if(!b?.length)return!1;let M=We(m),ne=s.get(y);return b.some(de=>{if(!M.startsWith(de))return!1;let De=We(M.slice(de.length));return!!De&&!!ne?.has(De)})};for(let y of e||[]){let m=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||(pe.length=0,!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length))break;{let b=m.note||El(m.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let M=String(b.summary||"").match(p);if(!b.toolUseId&&M&&c.has(M[1]))break}if(b.kind==="command"&&tk(b.command))break;if(b.kind==="resume-summary"){t.push({id:y.id,kind:"resume-summary",title:b.title||"Context resumed from summary",bullets:b.bullets||[],sections:b.sections||[],rawChars:b.rawChars??null,ts:fe(y),box:y.boxId,agent:y.provider});break}b.kind!=="suppress"&&t.push({id:y.id,kind:"event",type:rk(b),name:b.name??null,command:b.command??null,summary:b.summary??null,status:b.status??null,ts:fe(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${fe(y)}`)||y.source==="transcript"&&at(y.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){t.push({id:y.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:fe(y),box:y.boxId,agent:y.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"){m.kind==="prompt-answer"&&(G=null);break}for(let b of B.values())b.status="stale";B.clear();for(let b of _.values())b.status="stale";_.clear();for(let b of W.values())b.status="stale";W.clear(),G&&G.box===y.boxId&&(t.push(RM(G)),G=null),t.push({id:y.id,kind:"message",role:"user",text:m.text,ts:fe(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...y.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let b=m.from||{};t.push({id:y.id,kind:"event",type:"peer-message",text:m.text,from:b.trackName||b.boxId||b.name||null,fromTrackId:b.trackId||null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let b=gb(m,{codexFailedText:ao});if(b){t.push({id:y.id,kind:"event",type:"provider-error",cls:b.cls,status:b.status??void 0,text:b.text,provider:y.provider,legacy:!0,at:fe(y),ts:fe(y),box:y.boxId,agent:y.provider});break}A();let M=(m.images||[]).filter(Re=>!k(Re,fe(y)));if(!String(m.text||"").trim()&&!M.length)break;if(y.provider==="cursor"){let Re=`${y.boxId}|${m.text}`;if(n.has(Re))break;n.add(Re)}(!G||G.box===y.boxId)&&(G=null);let ne=y.provider==="codex"?(m.tools||[]).filter(Re=>!Af.has(Re)&&!za.has(Re)):m.tools||[],de=m.text,De=null,ht=zf(String(m.text||""));ht&&(de=ht.text,De={questions:ht.questions}),t.push({id:y.id,kind:"message",role:"assistant",text:de,ts:fe(y),box:y.boxId,agent:y.provider,boxRole:m.boxRole,...De?{ask:De}:{},...ne.length?{tools:ne}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...M.length?{images:M}:{}}),L(y),De&&(G={id:y.id,groupId:Yf(y.provider,m.sourceId||y.id,De.questions),provider:y.provider,box:y.boxId,questions:De.questions,ts:fe(y)});break}case"job.started":{if(!m.id)break;let b=fe(y),M=i.get(m.id);if(!M&&(m.kind||"agent")==="agent"&&m.name){let Re=l.get(m.name);Re&&Re.ts>=b&&(M=Re)}let ne={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:M?M.status==="killed"?"stopped":M.status&&M.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:M?.ts??null,summary:M?.summary??null,resultHead:M?.resultHead??null},de=_t.get(y.boxId),De=de?.jobs[de.jobs.length-1]?.startedTs;if(de&&De!=null&&b-De<=Ge)de.jobs.push(ne);else{let Re={id:y.id,kind:"job-dock",dockId:m.id,ts:b,box:y.boxId,agent:y.provider,jobs:[ne]};_t.set(y.boxId,Re),t.push(Re)}break}case"handoff.done":t.push(dt(y,"lead-handoff"));break;case"subtask.delegated":t.push({...dt(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":t.push(dt(y,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let M of B.values())M.status="stale";B.clear();for(let M of _.values())M.status="stale";_.clear()}let b=dt(y,`pr-${m.change}`);if(b.box=y.boxId??null,OM.has(m.change)&&m.number!=null){let M=CM(S,E,m);if(S.size>=2){for(let de of E.values())for(let De of de)De.grouped=!0;b.groupPrs=[...S.values()];let ne=[b];for(let de of S.keys())E.set(de,ne)}else{let ne=E.get(M)??[];ne.push(b),E.set(M,ne)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let M=ck(m),ne=R&&!R.complete||S.size>=2;if(S.has(M))S.delete(M),E.delete(M);else if(m.number!=null)for(let de of[...S.keys()])de.endsWith(`#${m.number}`)&&(S.delete(de),E.delete(de));if(ne&&m.number!=null){(!R||R.complete)&&(R={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:fe(y),ts:fe(y),box:y.boxId,agent:y.provider},t.push(R)),R.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:fe(y)}),R.remaining=[...S.values()],R.complete=S.size===0;break}}t.push(b);break}case"pr.offered":{if(!m.offerId)break;for(let M of B.values())M.status="stale";B.clear();for(let M of _.values())M.status="stale";_.clear();let b={id:y.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:y.source==="cli"||m.via==="cli"?"cli":"transcript",ts:fe(y),box:y.boxId,agent:y.provider};B.set(m.offerId,b),T.set(m.offerId,b),t.push(b),b.source==="cli"&&ie(b);break}case"pr.offer.answered":{let b=T.get(m.offerId);b&&(b.status=m.action||"dismiss",b.actions=Array.isArray(m.actions)?m.actions:null,b.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(b.existingPr={number:m.existingPr.number??null}),B.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let M of _.values())M.status="stale";_.clear();let b={id:y.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};_.set(m.offerId,b),O.set(m.offerId,b),t.push(b);break}case"pr.exists.answered":{let b=O.get(m.offerId);b&&(b.status=m.choice||"dismiss",_.delete(m.offerId));break}case"box.state.changed":{let b=m.change==="model"?"model-change":m.change;if(!b)break;let M=dt(y,b);if(b==="model-change"&&(M.to=m.model),b==="node-update"&&M.latest){let ne=t.findIndex(de=>de.kind==="event"&&de.type==="node-update"&&de.latest===M.latest);if(ne>=0){let de=t[ne];t[ne]={...M,ts:de.ts,at:de.at,firstAt:de.firstAt??de.ts,updatedAt:M.ts};break}}t.push(M);break}case"track.transferred":case"track.recovered":t.push(dt(y,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){d(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;f(`view:${m.viewId}`,{id:y.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:fe(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{t.push({id:y.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:fe(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(m.change==="picked"){t.push({id:y.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:fe(y),box:y.boxId,agent:y.provider});break}if(m.change==="deleted"){d(`mock:${m.mockId}`);break}if(m.change!=="created")break;f(`mock:${m.mockId}`,{id:y.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(m.change==="deleted"){d(`flow:${m.flowId}`);break}if(m.change!=="created")break;f(`flow:${m.flowId}`,{id:y.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:fe(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){d(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;f(`runbook:${m.runbookId}`,{id:y.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:fe(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{t.push({id:y.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let M of H.values())M.box===(m.box||y.boxId)&&(M.status="stale",H.delete(M.offerId));let b={id:y.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:fe(y),box:m.box||y.boxId,agent:m.agent||y.provider};H.set(m.offerId,b),ae.set(m.offerId,b),t.push(b);break}case"box.fullscreen.answered":{let b=ae.get(m.offerId);b&&(b.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",H.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let M of I.values())M.key===m.key&&(M.status="stale",I.delete(M.offerId));let b={id:y.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};I.set(m.offerId,b),U.set(m.offerId,b),t.push(b);break}case"ticket.move.answered":{let b=U.get(m.offerId);b&&(b.status=m.action==="moved"?"moved":"dismiss",I.delete(m.offerId));break}case"ticket.state.changed":{for(let b of I.values())b.key===m.key&&b.to===m.to&&(b.status="stale",I.delete(b.offerId));if(m.mode==="offer")break;t.push(dt(y,"ticket-state"));break}case"ticket.commented":{t.push({id:y.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:fe(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":t.push(dt(y,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let M of X.values())M.status="stale";X.clear();let b={id:y.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};X.set(m.proposalId,b),C.set(m.proposalId,b),t.push(b),ie(b);break}case"ticket.create.answered":{let b=C.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",b.createdCount=m.createdCount??null,b.failedCount=m.failedCount??null,X.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let M of Q.values())M.status="stale";Q.clear();let b={id:y.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:fe(y),box:y.boxId,agent:y.provider};Q.set(m.proposalId,b),ve.set(m.proposalId,b),t.push(b),ie(b);break}case"loop.create.answered":{let b=ve.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",Q.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let b=m.replaces?se.get(m.replaces):null;b&&b.status==="armed"&&(b.status="replaced");let M={id:y.id,kind:"followup-armed",armId:m.armId,trigger:m.trigger??"now",label:m.label??"",prompt:m.prompt??"",why:m.why??null,status:"armed",ts:fe(y),box:y.boxId,agent:y.provider};se.set(m.armId,M),M.trigger!=="now"&&t.push(M);break}case"followup.armed.cancelled":{let b=se.get(m.armId);b&&b.status==="armed"&&(b.status="cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let ne of W.values())ne.trigger===m.trigger&&(ne.status="stale",W.delete(ne.offerId));let b=Array.isArray(m.rows)?m.rows:[];for(let ne of b){let de=ne.armId?se.get(ne.armId):null;de&&de.status==="armed"&&(de.status="offered")}let M={id:y.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},rows:b.map(ne=>({followupId:ne.followupId??null,armId:ne.armId??null,label:ne.label??"",prompt:ne.prompt??null,promptPreview:ne.promptPreview??null,promptHash:ne.promptHash??null,origin:ne.origin??"operator",why:ne.why??null})),status:"open",answer:null,ts:fe(y),box:y.boxId,agent:y.provider};W.set(m.offerId,M),be.set(m.offerId,M),t.push(M),M.trigger==="now"&&ie(M);break}case"followup.answered":{let b=be.get(m.offerId);b&&(b.status=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss",b.answer={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0},W.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let M of ce.values())M.status="stale";ce.clear();let b={id:y.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:fe(y),box:y.boxId,agent:y.provider};ce.set(m.proposalId,b),ue.set(m.proposalId,b),t.push(b),ie(b);break}case"followup.create.answered":{let b=ue.get(m.proposalId);b&&(b.status=m.action==="created"?"created":"dismiss",ce.delete(m.proposalId));break}case"followup.created":{let b=m.proposalId?ue.get(m.proposalId):null;b&&(b.created={followupId:m.followupId??null});break}case"loop.created":{let b=m.proposalId?ve.get(m.proposalId):null;b&&(b.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(y,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let ne of Ae.values())ne.loopId===m.loopId&&(ne.status="stale",ne.staleReason="superseded",Ae.delete(ne.proposalId));let b=y.type==="loop.update.offered",M={id:y.id,kind:b?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...b?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:fe(y),box:y.boxId,agent:y.provider};Ae.set(m.proposalId,M),_e.set(m.proposalId,M),t.push(M),ie(M);break}case"loop.update.answered":{let b=_e.get(m.proposalId);b&&(m.action==="moved"?(b.status="stale",b.staleReason="moved"):b.status=m.action==="applied"?"applied":"dismiss",Ae.delete(m.proposalId));break}case"loop.delete.answered":{let b=_e.get(m.proposalId);b&&(m.action==="moved"?(b.status="stale",b.staleReason="moved"):b.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",Ae.delete(m.proposalId));break}case"loop.updated":{let b=m.proposalId?_e.get(m.proposalId):null;b&&(b.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(y,"loop-updated"));break}case"loop.deleted":t.push(dt(y,"loop-deleted"));break;case"loop.paused":t.push(dt(y,"loop-paused"));break;case"rework.cause.offered":{if(!m.sha||xt.has(m.sha))break;let b={id:y.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,status:"open",cause:null,ts:fe(y),box:y.boxId,agent:y.provider};xt.set(m.sha,b),t.push(b);break}case"rework.cause.answered":{let b=m.sha?xt.get(m.sha):null;b&&(b.status="answered",b.cause=m.cause??null);break}case"ticket.created":{let b={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},M=m.proposalId?C.get(m.proposalId):null;if(M){M.created=b;break}t.push({id:y.id,kind:"ticket-created",...b,ts:fe(y),box:y.boxId,agent:y.provider});break}case"error":{if(!m.kind)break;t.push(dt(y,m.kind));break}case"provider.error":t.push({...dt(y,"provider-error"),provider:y.provider,box:y.boxId,agent:y.provider});break;case"usage.cap.hit":t.push(dt(y,"usage-cap-hit"));break;case"usage.cap.warning":t.push(dt(y,"usage-cap-warning"));break;case"provider.connected":{t.push({id:y.id,kind:"event",type:"provider-connected",provider:m.provider||y.provider,ts:fe(y),at:fe(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(G=null,m.control||!m.question)break;let b=m.groupId||m.promptId;if(!b)break;let M=je.get(b);M||(M={id:y.id,kind:"prompt",groupId:b,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:m.questionTotal??1,questions:[],ts:fe(y)},je.set(b,M));let ne=m.questionIndex??0,de={index:ne,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},De=M.questions.findIndex(Re=>Re.index===ne&&We(Re.question)===We(de.question));De>=0?M.questions[De]=de:M.questions.push(de),M.questions.sort((Re,ya)=>Re.index-ya.index),M.questionTotal=Math.max(M.questionTotal,m.questionTotal??1),M.questions.length>=M.questionTotal&&(t.push(M),je.delete(b));break}default:break}}let nt=u.size?t.filter(y=>!y.retired):t;return NM(nt.sort((y,m)=>(y.ts??0)-(m.ts??0)))}function NM(e){let t=new Map,n=[];for(let r of e){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=t.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=MM){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};t.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else t.delete(r.box),n.push(r)}return n}function Zf(e,t=null){let n=[];for(let o of e||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:PM(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(t??wr(e)).filter(o=>o.kind==="message");return lb(n,r)}function PM(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ps(e.command)?t:mr(e.command,e.description||"")}function dt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:fe(e),ts:fe(e)}}function lk(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+DM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&BM.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function uk(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!LM.has(r.type)||!r.box)continue;let o=(r.ts??0)+jM,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function dk(e){return(e||[]).filter(t=>FM.has(t?.kind)&&t.status==="open")}var fe,OM,ck,MM,DM,BM,jM,LM,FM,Qf=j(()=>{ms();io();dl();bl();Vf();Xf();fe=e=>e.payload?.ts??e.ts;OM=new Set(["opened","ci-fixed","reconciled","pushed"]),ck=e=>`${e.repo??""}#${e.number}`;MM=2*6e4;DM=12e4,BM=new Set(["archived","unarchived","opened"]);jM=2500,LM=new Set(["pr-opened","pr-merged","pr-auto-armed"]);FM=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","fullscreen-offer","followup-create-offer"])});function KM(e){let t=null,n=null,r=null;for(let o of e??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(t={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:t,lastPush:n,lastEventAt:r}}function VM(e){for(let t=(e?.length??0)-1;t>=0;t--){let n=e[t];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function zM(e,t){let n=[],r=[],o=new Set,s=new Set;for(let a=(e?.length??0)-1;a>=0;a--){let i=e[a];if(i?.type!=="tool.called")continue;if(t!=null&&(i.ts??0)<=t)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<WM&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<qM&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function hk(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=KM(e),s=VM(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=wr(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-UM);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(mk[r]??mk.failover),l.push(""),o.lastPush?.sha){let d=pk(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=pk(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=zM(e??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${GM(d.text,HM)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
|
|
151
|
+
`);return f.length<=fk?f:`${f.slice(0,fk)}
|
|
152
|
+
|
|
153
|
+
[\u2026 brief truncated]`}var UM,HM,fk,WM,qM,GM,pk,mk,wk=j(()=>{Qf();UM=12,HM=2e3,fk=48e3,WM=30,qM=10;GM=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},pk=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;mk={failover:"**This is a reconstruction, not a checkpoint.** The node this Track lived on went offline without a clean handoff; bostrat assembled this brief from the durable event log. Treat every claim below as a lead, not a fact \u2014 **verify the repository state first** (git status, git log, the diff against the last pushed ref) before continuing work.","in-place":"**This is a reconstruction, not a checkpoint.** The previous lead ended without writing one (out of credits, capped, or unresponsive); bostrat assembled this status from the Track's event log. You are in the SAME live workspace \u2014 uncommitted work is present locally. **Run git status and git log first** and treat every claim below as a lead, not a fact.",get recover(){return this.failover},transfer:"**This Track was MOVED to a new machine while it was alive.** The previous node checkpointed and pushed its work as part of the move; this workspace was re-created from that push (uncommitted work re-applied when possible). **Run git status and git log to confirm what carried over**, then continue the objective below."}});import YM from"node:fs";import gr from"node:fs/promises";import yr from"node:path";function Tk(e){let t=no(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=gk?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,gk).toString("utf8").replace(/�+$/,"")}
|
|
154
|
+
|
|
155
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function oN(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(bk.test(s)){if(r.length>=Rk)continue;let a=rN(s.replace(bk,"").replace(/[.;,:]+$/,""),Ok,tN);a&&r.push(a)}else r.length||n.push(s);let o=n.join(" ").replace(/\s+/g," ").replace(/[:\-–—]+$/,"").trim();if(!o)throw new Error("beat file has no summary line");return o.length>yk&&(o=`${o.slice(0,yk-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Ck(e,t,{timeoutMs:n=9e4}={}){let r=yr.join(t,QM),o=yr.join(r,eN);await gr.mkdir(r,{recursive:!0});let s=await Al(o),a=await ge(e);if(!a)throw new Error(`session "${e}" not found`);return await _l(e,a,nN(o)),await Lk(o,s,n),oN(await gr.readFile(o,"utf8"))}function Mk(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Ak||n===$k;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(Ek)||n===JM||r.startsWith(_k)||r.startsWith(Ik)}function Nk(e){let t=[];for(let r of e?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-sN;t.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&t.push({box:r.to,from:s,to:o})}let n=e?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&t.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&t.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),t}function Pk(e,t){let n=[];for(let r of e?.boxes||[]){let o=t?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function Dk(e,{windows:t=[]}={}){if(Mk(e.role,e.text))return!0;if(e.role!=="assistant"||!e.ts)return!1;for(let n of t)if(e.box===n.box&&e.ts>=n.from&&e.ts<=n.to)return!0;return!1}function Il(e){return ep.get(e)||null}async function Bk(e){let t=ep.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ge(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:yr.join(n.cwd,vk,Sk)};return ep.set(e,r),cN(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function cN(e,t){e.step="summarize",await jk(e.name,t),e.step="clear";let n=await ge(e.name);await _l(e.name,n,"/clear"),await rp(aN),e.step="resume",await _l(e.name,n,ZM),e.step="done",e.finishedAt=Date.now()}async function jk(e,t,{timeoutMs:n=iN}={}){let r=yr.join(t,vk),o=yr.join(r,Sk);await gr.mkdir(r,{recursive:!0});let s=await Al(o),a=await ge(e);await re({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await _l(e,a,XM(o)),await Lk(o,s,n),await re({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Al(o)});let i=await zb(t,o,{exclude:u=>Mk(u.role,u.text)});await re({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await $({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await gr.readFile(o,"utf8");await $({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Tk(u),ts:Date.now()}})}catch{}}await Qd(t);let l=await lN(r,o);return c&&await Oy(c,l||o,{box:e,agent:a?.agent??null}),o}async function tp(e,{timeoutMs:t}={}){let n=await ge(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return jk(e,n.cwd,t?{timeoutMs:t}:void 0)}async function np(e,t,{trackName:n="this Track"}={}){let r=await F({id:e,cwd:t}),o=hk(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=yr.join(t,kk);return await gr.mkdir(yr.dirname(s),{recursive:!0}),await gr.writeFile(s,o,"utf8"),await $({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Tk(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),kk}async function Al(e){try{return(await gr.stat(e)).mtimeMs}catch{return 0}}async function Lk(e,t,n){let r=Date.now()+n;for(;;){if(await Al(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await rp(2e3)}}async function lN(e,t){try{if(!YM.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=yr.join(e,`${n}.md`);return await gr.copyFile(t,r),r}catch{return null}}function rp(e){return new Promise(t=>setTimeout(t,e))}function xk(e){return e==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":""}async function _l(e,t,n){if(t){let o=xk(t.agent);await hr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ge(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Le(e,["Escape"],{kind:"drive-wipe"}),await rp(150),await ls(e,cl,{kind:"drive-wipe"}),await Ze(e,n,{kind:"orchestration-drive",...pn(r.agent,n)}),await ni({sessionName:e,agent:r.agent,agentCmd:xk(r.agent),text:n,flow:"orchestration-drive"})}var vk,Sk,gk,Ek,Ak,_k,JM,XM,ZM,Ik,$k,QM,eN,yk,Rk,Ok,tN,nN,bk,rN,sN,aN,iN,ep,kk,op=j(()=>{lt();Za();dn();Qa();Uf();Yb();Oa();me();Qc();wk();vk=".bostrat/handoff",Sk="latest.md",gk=32*1024;Ek="Checkpoint: write a work-in-progress handoff",Ak="HANDOFF WRITTEN",_k="Resume from .bostrat/handoff/latest.md",JM="/clear",XM=e=>`${Ek} to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${Ak} and take no further action.`,ZM=`${_k}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Ik="Beat: write a",$k="BEAT WRITTEN",QM=".bostrat/arc",eN="beat.txt",yk=200,Rk=4,Ok=5,tN=36,nN=e=>`${Ik} short summary of what this workstream accomplished, to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${Rk} further lines, each starting with "- " and naming ONE piece in at most ${Ok} words \u2014 a label, not a sentence (e.g. "- stale working flag on poll"). Write nothing else in the file. When the file is written, reply only with ${$k} and take no further action.`,bk=/^(?:[-–—*•·]|\d+[.)])\s+/,rN=(e,t,n)=>{let r=e.split(/\s+/).filter(Boolean).slice(0,t).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};sN=6e5;aN=3500,iN=3e5,ep=new Map;kk=".bostrat/handoff/status-latest.md"});function kr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!$l.test(String(e)))throw new Error(`pricing.js: ${n} window ${t} must be YYYY-MM-DD, got ${JSON.stringify(e)}`);let r=Date.parse(`${e}T${t==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${t} is not a real date: ${e}`);return r}function Hk(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:kr(o.from,"from",n),toMs:kr(o.to,"to",n)}))}return t}function ap(e){if(e==null)return Date.now();if(typeof e=="number")return Number.isFinite(e)?e:null;if(e instanceof Date){let n=e.getTime();return Number.isFinite(n)?n:null}let t=Date.parse(String(e));return Number.isFinite(t)?t:null}function dN(e){if(!e)return null;let t=String(e).trim();return br[t]?t:Fk[t]?Fk[t]:Object.keys(br).find(r=>t.startsWith(r))||null}function fN(e){if(!e||e===br)return uN;let t=Uk.get(e);return t||(t=Hk(e),Uk.set(e,t)),t}function Wk(e,t,n={}){let{table:r,speed:o}=n,s=fN(r),a=r&&r!==br?s[e]?e:null:dN(e);if(!a)return null;let i=ap(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function mn(e,t={},n,r){let o=Wk(e,n,r);return o?((t.input||0)*o.input+(t.output||0)*o.output+(t.cacheWrite||0)*o.cacheWrite+(t.cacheRead||0)*o.cacheRead)/1e6:0}function pN(e=br){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:kr(a.from,"from",n),toMs:kr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),$l.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`),a.fast&&(typeof a.fast[i]!="number"||!Number.isFinite(a.fast[i])||a.fast[i]<0)&&t.push(`${n}: fast variant ${i} must be a non-negative number, got ${JSON.stringify(a.fast?.[i])}`)}}return t}function hN({now:e=Date.now(),table:t=br,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...mN,...n},a=[],i=ap(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(f=>({...f,fromMs:kr(f.from,"from",c),toMs:kr(f.to,"to",c),verifiedMs:$l.test(String(f.verifiedAt??""))?Date.parse(`${f.verifiedAt}T00:00:00.000Z`):NaN}))}catch(f){a.push(String(f.message));continue}for(let f of u){if(!Number.isFinite(f.verifiedMs)){a.push(`${c}: window ${f.from??"\u2026"} \u2192 ${f.to??"now"} has no verifiedAt`);continue}let d=i>=f.fromMs&&i<=f.toMs,p=f.fromMs>i&&f.fromMs-i<=o*sp;d&&i-f.verifiedMs>r*sp&&a.push(`${c}: current rate last verified ${f.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),p&&f.fromMs-f.verifiedMs>s*sp&&a.push(`${c}: scheduled rate change on ${f.from} was last verified ${f.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function bs({now:e=Date.now(),table:t=br,...n}={}){let r=ap(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=kr(l.from,"from",i),f=kr(l.to,"to",i);r>=u&&r<=f&&$l.test(String(l.verifiedAt??""))&&(!o||l.verifiedAt<o)&&(o=l.verifiedAt),u>r&&s.push({model:i,from:l.from,verifiedAt:l.verifiedAt??null})}s.sort((i,c)=>i.from<c.from?-1:1);let a=[...pN(t),...hN({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var br,Fk,$l,uN,Uk,qk,mN,sp,Dn=j(()=>{br={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Fk={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},$l=/^\d{4}-\d{2}-\d{2}$/;uN=Hk(br);Uk=new WeakMap;qk=(e,t,n)=>Wk(e,t,n)!==null;mN=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),sp=864e5});function wN(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Gk(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Ft(e){let t=e.payload?.model||"unknown",n=wN(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=mn(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!qk(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function xr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=Kk();for(let d of n){let{usd:p,model:h,tokens:w,repriced:g,unpriced:k,asRecorded:S}=Ft(d);i+=S;let E=d.provider||"unknown",R=d.boxId||"unattributed";if(g&&c++,k&&(l++,u.add(h)),a+=p,Gk(f,w),r[h]||={usd:0,tokens:Kk(),events:0},r[h].usd+=p,r[h].events+=1,Gk(r[h].tokens,w),o[E]||={usd:0,events:0},o[E].usd+=p,o[E].events+=1,s[R]||={usd:0,events:0,provider:E,models:new Set,label:t[R]||R},s[R].usd+=p,s[R].events+=1,h!=="unknown"&&s[R].models.add(h),d.epoch!=null){let B=(s[R].epochs||={})[d.epoch]||={usd:0,events:0,provider:E,models:new Set};B.usd+=p,B.events+=1,B.provider=E,h!=="unknown"&&B.models.add(h)}}return{totalUsd:ks(a),asRecordedUsd:ks(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Rl(r,d=>({...d,usd:ks(d.usd)})),byProvider:Rl(o,d=>({...d,usd:ks(d.usd)})),byBox:Rl(s,d=>({usd:ks(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Rl(d.epochs,p=>({usd:ks(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Kk,ks,Rl,vr=j(()=>{Dn();Kk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});ks=e=>Math.round(e*1e4)/1e4,Rl=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function gN(e){if(!Number.isFinite(e)||e<=0)return null;let t=e/864e5;if(t>=1)return`${Math.round(t)}d`;let n=e/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(e/6e4))}m`}function Vk(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,p]=f.split("#");return`${d.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(ip(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(ip(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${ip(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=xr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?gN(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var ip,zk=j(()=>{vr();ip=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as yN}from"node:child_process";import{promisify as bN}from"node:util";import{randomUUID as kN}from"node:crypto";async function cp(e,t){let n=Date.now()+t;for(;;){if(await Mn(e))return!0;if(Date.now()>n)return!1;await ii(vN)}}async function TN(e,t){let n=Jk[t]||Jk.default;n.escape&&(await Le(e,["Escape"],{kind:"swap-exit"}),await ii(150)),n.wipe&&await ls(e,cl,{kind:"swap-exit"}),await ll(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=pn(t).submitDelayMs||0;await ii(Math.max(r,250)),await Le(e,["Enter"],{kind:"swap-exit"})}async function Xk(e,t){try{let{stdout:n}=await lo(K,V("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Zk(e){await lo(K,V("respawn-pane","-k","-t",e))}async function lp(e,t={}){if(!Pe(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ge(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||dr(n),a=Number(t.exitTimeoutMs)||xN;await re({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Mn(e)&&(await TN(e,o),!await cp(e,a)&&(i=!0,await re({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Zk(e),!await cp(e,Yk))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&ro(n),l=c?Nc():null,u=await Xk(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await lo(K,V("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await lo(K,V("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?nl(g,l):g}if(f&&!i&&(await Zk(e),!await cp(e,Yk)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,p=t.resumeSessionId||kN(),h=d?n:Ua(n,p,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await or(r.cwd),s==="codex"&&await Jo(r.cwd),s==="cursor"&&await Xo(r.cwd),await oo(e),await lo(K,V("set-option","-t",e,"@bostrat-sid",p)),await lo(K,V("set-option","-t",e,"@bostrat-agent",s)),await ii(SN),c&&Fa().length&&(await Xk(e,La)||await lo(K,V("set-environment","-t",e,La,"1")).catch(()=>{}),await Ze(e,`export ${La}=1`,{kind:"swap-env"}),await ii(150)),await Ze(e,h,{kind:"swap-launch"}),await re({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:f,launchCmd:h}}var lo,ii,xN,Yk,vN,SN,Jk,Qk=j(()=>{lt();Ta();Dc();Bc();Qa();dn();lo=bN(yN),ii=e=>new Promise(t=>setTimeout(t,e)),xN=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Yk=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,vN=250,SN=400,Jk={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}}});async function e0(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Et(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
156
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let p=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let p=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(p?ps(p):null),detail:p?ps(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&EN.test(AN(d.content))&&s.add(d.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var EN,AN,t0=j(()=>{Xt();ms();EN=/^Async (?:agent|task) launched/i,AN=e=>{if(typeof e=="string")return e.trim();if(!Array.isArray(e))return"";let t=e.find(n=>n&&n.type==="text"&&typeof n.text=="string");return t?t.text.trim():""}});function Sr(e,t){if(!e)return null;let n=String(e);if(t){let o=t.endsWith("/")?t:`${t}/`;if(n===t)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}function _N(e){let t=typeof e=="string"?e:String(e?.patch??e?.input??""),n=[];for(let r of t.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function n0(e,t,n){switch(e){case"Shell":{let r=String(t?.command||"").trim();return r?{command:r,description:String(t?.description||"").trim()}:null}case"Read":{let r=t?.path;if(!r)return null;let o=Number(t?.offset)||null,s=o&&Number(t?.limit)?o+Number(t.limit)-1:null;return{command:Zt(`read ${Sr(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${xs(r)}`:`Read ${xs(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Sr(t.path,n):null,s=t?.glob?` --glob ${up(t.glob)}`:"",a=Zt(String(r),40);return{command:Zt(`grep ${up(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=t?.glob_pattern;if(!r)return null;let o=t?.target_directory?Sr(t.target_directory,n):null;return{command:Zt(`glob ${up(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Zt(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Zt(`edit ${Sr(t.path,n)}`,140),description:`Edit ${xs(t.path)}`}:null;case"Write":return t?.path?{command:Zt(`write ${Sr(t.path,n)}`,140),description:`Write ${xs(t.path)}`}:null;case"Delete":return t?.path?{command:Zt(`delete ${Sr(t.path,n)}`,140),description:`Delete ${xs(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Sr(s,n)).filter(Boolean);return{command:Zt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=_N(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Zt(`apply_patch ${Sr(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${xs(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:Zt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Ol,Zt,xs,up,r0=j(()=>{Ol=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Zt=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,xs=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);up=e=>`"${String(e).replace(/"/g,'\\"')}"`});import ci from"node:fs";import IN from"node:fs/promises";import uo from"node:path";import $N from"node:os";import RN from"node:crypto";import ON from"node:zlib";function a0(e){let t=RN.createHash("md5").update(String(e)).digest("hex");return uo.join(CN,t)}function i0(e){try{let t=JSON.parse(ci.readFileSync(uo.join(e,"meta.json"),"utf8"));return{createdAtMs:Number(t.createdAtMs)||null,updatedAtMs:Number(t.updatedAtMs)||Number(t.createdAtMs)||null,hasConversation:t.hasConversation===!0}}catch{return null}}function MN(e){let t=a0(e),n;try{n=ci.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=uo.join(t,o.name),a=uo.join(s,"store.db");if(!ci.existsSync(a))continue;let i=i0(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 Cl(e,t){if(!e)return null;if(t){let n=uo.join(a0(e),t),r=uo.join(n,"store.db");if(ci.existsSync(r))return{id:t,dir:n,store:r,...i0(n)||{createdAtMs:null,updatedAtMs:null}}}return MN(e)}function o0(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function NN(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=ON.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function PN(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 DN(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Ol.has(r.toolName))continue;let o=n0(r.toolName,r.args,t);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}async function dp(e,t){if(!s0)return vs;let{DatabaseSync:n}=s0,r;try{r=new n(e,{readOnly:!0})}catch{return vs}try{let o=r.prepare("SELECT value FROM meta WHERE key = '0'").get()||r.prepare("SELECT value FROM meta LIMIT 1").get();if(!o)return vs;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return vs}let a=s.lastUsedModel||null,i=s.latestRootBlobId?o0(r,s.latestRootBlobId):null;if(!i)return{...vs,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of PN(i)){let h=NN(o0(r,p));if(!h)continue;let w=h.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),h.role!=="assistant")continue;if(Array.isArray(h.content))for(let S of h.content){let E=S?.providerOptions?.cursor?.modelName;E&&(f=E)}l.push(...DN(h.content,t).map((S,E)=>({...S,id:S.id||`${p}:${E}`}))),u.push(...jN(h.content));let g=BN(h.content);if(!g)continue;let k=[...new Set(u)];u=[],c.push({id:p,text:g,tools:k.length?k:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return vs}finally{try{r.close()}catch{}}}function c0(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(ci.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 l0(e,t,{limit:n=80}={}){let r=Cl(e,t);if(!r)return[];let{turns:o,model:s}=await dp(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=c0(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 u0(e,t){let n=Cl(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await dp(n.store,e);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function d0(e,t,{limit:n=80}={}){let r=Cl(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await dp(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=c0(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:mr(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:mr(u.command,u.description),ts:i+1});return l}async function f0(e,t){let n=Cl(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await IN.stat(n.store)).mtimeMs)}catch{return null}}var CN,BN,jN,s0,vs,fp=j(()=>{ms();r0();CN=process.env.BOSTRAT_CURSOR_CHATS||uo.join($N.homedir(),".cursor","chats");BN=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
157
|
+
`).trim():"",jN=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],s0=process.getBuiltinModule?.("node:sqlite"),vs={turns:[],trailingCommands:[],model:null,requestIds:[]}});import LN from"node:fs/promises";function wp(e){return p0.set(e.agent,e),e}function li(e){return p0.get(e)||FN}async function yp(e,t={}){return(await li(e.agent).readMessages(e,t)).map(gp(e))}async function m0(e,t={}){return(await li(e.agent).readCommands(e,t)).map(gp(e))}async function h0(e,t={}){return(await li(e.agent).readJobs(e,t)).map(gp(e))}var Ss,pp,mp,hp,FN,p0,gp,w0=j(()=>{Xt();fl();ms();t0();fp();io();Ss=class{constructor(t,n=new Set){this.agent=t,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},pp=class extends Ss{transcript(t){return!t?.cwd||!t?.sessionId?null:Jt(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?If(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?cb(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?e0(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await LN.stat(n)).mtimeMs)}catch{return null}}},mp=class extends Ss{async readMessages(t,n){return l0(t.cwd,t.sessionId,n)}async readCommands(t,n){return d0(t.cwd,t.sessionId,n)}async updatedAt(t){return f0(t.cwd,t.sessionId)}},hp=class extends Ss{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return fb(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return pb(t.cwd,this.sessionId(t),n)}async updatedAt(t){return mb(t.cwd,this.sessionId(t))}},FN=new Ss("unknown"),p0=new Map;wp(new pp("claude",Sf));wp(new mp("cursor",Ol));wp(new hp("codex",za));gp=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function mo(e){let t=e.search(vp);return(t>=0?e.slice(0,t):e).trim()}function UN(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function HN(e){let t=String(e).trim();return!!(!t||po.test(e)||vp.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function WN(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=qN(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function qN(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(d=>d.length>3&&n.includes(d)).length;if(u)return u;let f=l.slice(0,8).trim();return f.length>=4&&n.includes(f)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function y0(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function KN(e,t=""){let n=String(e);if(!k0.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
158
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let f=r[u].match(fo);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!po.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:mo(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function VN(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
159
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!x0.test(n[r]))return null;let o=r-1;for(;o>=0&&n[o].trim()==="";)o--;let s=[];for(;o>=0;o--){let c=n[o].match(fo);if(!c)break;s.unshift({cursor:!!c[1],n:Number(c[2]),label:c[4].trim()})}if(s.length<2||s.some((c,l)=>c.n!==l+1))return null;let a=[];for(let c=o;c>=0&&a.length<6&&!(GN.test(n[c])||po.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:mo(c.label),description:""}));return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function zN(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
160
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(fo);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:t,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}function JN(e,t=""){let n=String(e);if(!bp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
161
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(YN.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let f=r[u].match(fo);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!po.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>bp.test(u.label)))return null;let i=s.map(u=>({label:mo(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}function Sp(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(fo);o&&t.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=t.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}function XN(e,t=""){let n=String(e);if(!v0.test(n)||!S0.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=Sp(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:mo(s.label),description:""}))}]}:null}function ZN(e,t=""){let n=String(e);if(!T0.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=Sp(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:mo(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}function eP(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!QN.test(o)||Tp.test(e[r]))break;n+=o}return n}function tP(e,t=""){let n=String(e);if(!Tp.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
164
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=eP(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 rP(e,t=""){let n=String(e);if(!kp.test(n)||!nP.test(n)||ho.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
165
|
+
`)].reverse().find(a=>kp.test(a))||"OAuth error").trim();return{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}function oP(e,t=""){let n=String(e);if(!E0.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o=Sp(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:mo(s.label),description:""}))}]}:null}function sP(e,t=""){let n=String(e);if(ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
167
|
+
`),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match(A0);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(po.test(r[l])||vp.test(u))break;a.unshift(u)}let i=s[1].trim();i&&a.push(i);let c=a.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${c}`.slice(0,200),questions:[{question:c,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function aP(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(xp.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 iP(e,t=""){let n=String(e);if(!b0.test(n)||!xp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
|
+
`),o=aP(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let O=i;O<c;O++){let I=r[O].match(fo);I&&(u<0&&(u=O),l.push({line:O,cursor:!!I[1],checked:I[3]!=null&&I[3]!==" ",hasCheckbox:I[3]!=null,label:I[4].trim(),desc:""}))}if(!l.length)return null;for(let O of l)for(let I=O.line+1;I<Math.min(r.length,O.line+4)&&!(fo.test(r[I])||po.test(r[I]));I++){let U=r[I].trim();if(!(!U||HN(r[I])||g0.test(U))){O.desc=UN(U);break}}let f=l.filter(O=>!g0.test(O.label));if(!f.length)return null;let d=f.some(O=>O.hasCheckbox),p=Math.max(0,f.findIndex(O=>O.cursor)),h=s>=0?s+1:0,w=[];for(let O=h;O<u;O++){let I=r[O].trim();I&&!po.test(r[O])&&!/[☐☒]/.test(r[O])&&w.push(I)}let g=w.join(" ")||(s>=0?y0(r[s]):"")||"The agent is asking a question",k=s>=0?WN(r[s],g):null,S=k?.currentTabLabel||(s>=0?y0(r[s]):""),E=f.map(O=>({label:mo(O.label),description:O.desc})),R=E.map(O=>O.label).join("|"),T=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${S}:${g}:${R}`.slice(0,240),_=k?`claude:ask:${k.tabs.map(O=>O.label).join("|")}`.slice(0,240):T;return{provider:"claude",box:t,multiSelect:d,cursorIndex:p,promptId:T,groupId:_,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:g,header:S,multiSelect:d,options:E}]}}function wo(e,t){let n=String(t||"");return n?e==="claude"?b0.test(n)&&xp.test(n)||k0.test(n)||bp.test(n)||x0.test(n.trimEnd().split(`
|
|
169
|
+
`).pop()||"")?!0:ho.test(n)?!1:v0.test(n)&&S0.test(n)||T0.test(n)||Tp.test(n)||kp.test(n)||E0.test(n)||A0.test(n.trimEnd().split(`
|
|
170
|
+
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function Tr(e,t){return!e||!t?null:e.agent==="claude"?zN(t,e.name)||iP(t,e.name)||KN(t,e.name)||JN(t,e.name)||rP(t,e.name)||tP(t,e.name)||ZN(t,e.name)||XN(t,e.name)||oP(t,e.name)||sP(t,e.name)||VN(t,e.name):e.agent==="cursor"||e.agent==="codex"?$b(e.agent,t,e.name):null}async function Ep(e){if(!e)return null;let t=await Nn(e.name).catch(()=>"");return Tr(e,t)}function _0(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 I0(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 b0,xp,fo,g0,po,vp,k0,x0,GN,bp,YN,ho,v0,S0,T0,Tp,QN,kp,nP,E0,A0,Ap=j(()=>{lt();ml();b0=/to navigate\b.*\bEsc to cancel|Enter to select\b/,xp=/Chat about this/i,fo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,g0=/^(type something|chat about this|submit)\b/i,po=/^[─—-]{6,}\s*$/,vp=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;k0=/^\s*Switch model\?\s*$/m;x0=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,GN=/^\s*[─—▔-]{6,}\s*$/;bp=/Resume from summary/i,YN=/^\s*This session is\b/;ho=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;v0=/Choose the text style that looks best with your terminal/,S0=/Let's get started\./;T0=/Select login method:/;Tp=/Paste code here if prompted/,QN=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;kp=/OAuth error:/,nP=/Press Enter to retry\./;E0=/Quick safety check: Is this a project you created or one you trust/;A0=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Ml(e,t,n){if(!e||!t)return null;for(let r of cP)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function uP(e){let t=_p.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},_p.set(e,t)),t}function O0(e,t,n=Date.now()){let r=uP(e);if(r.sentAt=r.sentAt.filter(s=>n-s<R0),r.sentAt.length>=$0)return{action:"give-up"};if(n-r.last<lP)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 C0(e,t=Date.now()){let n=_p.get(e);return n?n.sentAt.filter(r=>t-r<R0).length>=$0:!1}var cP,lP,$0,R0,_p,Ip=j(()=>{cP=[{provider:"claude",id:"fullscreen-upsell",match(e,t){let n=e?.questions?.[0],r=(n?.options||[]).map(s=>String(s.label||"").trim());if(r.length!==2||!/^Yes, try it$/.test(r[0])||!/^Not now$/.test(r[1]))return!1;let o=`${n?.question||""}
|
|
171
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];lP=5e3,$0=5,R0=3600*1e3,_p=new Map});function Ts(e){return typeof e=="string"?e.trim():""}var M0=j(()=>{});import{execFile as dP}from"node:child_process";import{promisify as fP}from"node:util";async function Ue(e,t,{input:n}={}){let r=Rp("git",["-C",e,...t],{maxBuffer:N0});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Es(e){if(!e)return!1;try{let{stdout:t}=await Ue(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function P0(e){let{stdout:t}=await Ue(e,["rev-parse","--show-toplevel"]);return t.trim()}async function pP(e){try{return await Ue(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function As(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Ue(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Rp("git",["-C",e,...a],{maxBuffer:N0,env:o,...i});try{let a=await pP(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 Rp("rm",["-f",r])}catch{}}}async function Op(e,t,{trailers:n=[]}={}){let{stdout:r}=await Ue(e,["status","--porcelain"]);return r.trim()?(await Ue(e,["add","-A"]),await Ue(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function ui(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Ue(e,["status","--porcelain"]),r=n.split(`
|
|
172
|
+
`).filter(Boolean);return t&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function di(e,t){if(!t)return 0;try{let{stdout:n}=await Ue(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function _s(e,t,n,r){return await Ue(e,["worktree","add","--no-track","-b",n,t,r]),t}async function D0(e,t){try{return await Ue(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function Nl(e,t,n){return await Ue(e,["worktree","add",t,n]),t}async function Is(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Ue(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 Ue(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Cp(e){await Ue(e,["init","-q"]),await Ue(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Rt(e,t){try{await Ue(e,["worktree","remove","--force",t])}catch{try{await Ue(e,["worktree","prune"])}catch{}}}function mP(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 Mp(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Np(e,t,n,r){let{stdout:o}=await Ue(e,Mp(["diff","--shortstat",t,n],r));return mP(o)}async function Pp(e,t,n,r){let{stdout:o}=await Ue(e,Mp(["diff","--name-only",t,n],r));return o.split(`
|
|
173
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Dp(e,t,n,r){let{stdout:o}=await Ue(e,Mp(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await $p(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await $p(e,o,["--3way","--whitespace=nowarn"]),a=await hP(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await $p(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function $p(e,t,n){try{return await Ue(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function hP(e){try{let{stdout:t}=await Ue(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
174
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Rp,N0,Pl=j(()=>{Rp=fP(dP),N0=64*1024*1024});function Dl(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!wP.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,wP,Bn=j(()=>{Ve=e=>!!(e&&(e.archivedAt||e.transferredTo)),wP=new Set(["merged","conflict","canceled","failed","handed-off"])});import Bp from"node:fs/promises";import j0 from"node:path";import gP from"node:os";function H0(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function W0(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function q0(e){let t=new Date(H0(e));return t.setDate(t.getDate()+7),t.getTime()}function K0(e){let t=new Date(W0(e));return t.setMonth(t.getMonth()+1),t.getTime()}function jl(e=Date.now()){for(let[t,n]of Object.entries($s)){let r=n.startOf(e);if(r!==jn[t]){jn[t]=r;for(let o of go.values())o[t]=0;for(let o of Rs)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Rs.delete(o)}}}function G0(e,t){let n=Qt?.caps?.[e]?.[$s[t].capKey];return Number.isFinite(n)&&n>0?n:null}function yo(e,t=Date.now()){if(!e||!Lp||!Qt?.caps)return null;jl(t);let n=go.get(e);for(let r of Object.keys($s)){let o=G0(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Bl(s),resetsAt:$s[r].nextStart(t)}}return null}function V0(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${jn[t.window]}`;return Rs.has(n)?!1:(Rs.add(n),!0)}function B0(e,t,n,r=Date.now()){if(!e||!t)return;jl(r);let o=yP(e);for(let[s,a]of Object.entries($s))n>=jn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function bP(e,t,n=Date.now()){if(!Qt?.caps||!jp)return;let r=go.get(e);if(r)for(let[o,s]of Object.entries($s)){let a=G0(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*U0||i>=a)continue;let c=`warn:${e}:${o}:${jn[o]}`;if(!Rs.has(c)){Rs.add(c);try{let l=await jp(t);if(!l)continue;await $(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Bl(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function z0(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 Ll(e,{source:t="local",updatedAt:n=Date.now()}={}){Qt={caps:z0(e),updatedAt:n,source:t};try{await Bp.mkdir(L0,{recursive:!0}),await Bp.writeFile(F0(),JSON.stringify(Qt,null,2))}catch{}return fi()}function fi(e=Date.now()){jl(e);let t=new Set([...go.keys(),...Object.keys(Qt?.caps||{})]),n={};for(let r of t){let o=go.get(r);n[r]={weeklyUsd:Bl(o?.weekly||0),monthlyUsd:Bl(o?.monthly||0)}}return{caps:Qt?.caps||{},configured:!!Qt,seeded:Lp,updatedAt:Qt?.updatedAt??null,spent:n,windows:{weekly:{start:jn.weekly,resetsAt:q0(e)},monthly:{start:jn.monthly,resetsAt:K0(e)}},warnPct:U0}}async function Y0({listTracks:e,getTrack:t}={}){jp=t||null,jl();try{let o=JSON.parse(await Bp.readFile(F0(),"utf8"));Qt={caps:z0(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{Qt=null}let n=Date.now();ct.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ft(o);B0(o.provider||"unknown",s,o.ts??Date.now()),bP(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 F(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||B0(a.provider||"unknown",Ft(a).usd,i,n)}}return Lp=!0,fi()}var L0,F0,U0,$s,Qt,Lp,jn,go,Rs,jp,yP,Bl,Fl=j(()=>{me();vr();L0=process.env.BOSTRAT_STATE_DIR||j0.join(process.env.HOME||gP.tmpdir(),".bostrat"),F0=()=>j0.join(L0,"usage-caps.json"),U0=.8;$s={weekly:{startOf:H0,nextStart:q0,capKey:"weeklyUsd"},monthly:{startOf:W0,nextStart:K0,capKey:"monthlyUsd"}},Qt=null,Lp=!1,jn={weekly:0,monthly:0},go=new Map,Rs=new Set,jp=null,yP=e=>{let t=go.get(e);return t||(t={weekly:0,monthly:0},go.set(e,t)),t};Bl=e=>Math.round(e*1e4)/1e4});import kP from"node:fs/promises";import xP from"node:os";import J0 from"node:path";function rx(e){return e==="codex"?EP:e==="cursor"?nx:ex}function X0(e){let t=String(e||"").split(`
|
|
175
|
+
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function Z0(e){let t=String(e||"").split(`
|
|
176
|
+
`),n=t.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function AP(e){let t=String(e||"").split(`
|
|
177
|
+
`);for(let n=t.length-1;n>=0;n--){let r=t[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function Q0(e){let t=String(e||"").split(`
|
|
178
|
+
`),n=t.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(t[n]),o=[];for(let s=n+1;s<t.length;s++){let a=t[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function ox(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim();if(e==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!tx.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=nx.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!TP.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function pi(e,t,{timeoutMs:n=vP}={}){let r=Date.now()+n;for(;;){let o=await Ce(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Ul(SP)}}async function _P(e,t){return await Ze(e,`/model ${t.model}`,pn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function IP(e,t){await il(e),await Ze(e,"/model",{enter:!1}),await Ul(250),await Le(e,["Enter"],{kind:"model-switch"});let n=await pi(e,X0);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Le(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=AP(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Ze(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await pi(e,i=>Z0(i)||!X0(i)&&o(i)&&{done:o(i)});if(!s)throw await Le(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?tx.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Le(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Ze(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await pi(e,i=>!Z0(i)&&o(i));if(!a)throw new Error("codex never confirmed the model change");return{verified:!0,model:a.model,effort:a.effort}}async function $P(){let e=J0.join(process.env.CURSOR_CONFIG_DIR||J0.join(xP.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await kP.readFile(e,"utf8"))?.model||null}catch{return null}}async function RP(e,t){await il(e),await ll(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await pi(e,Q0),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(t.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await pi(e,Q0,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await Le(e,["Down"],{kind:"model-switch"}),await Ul(250)}if(!n||!s||!o(s)){await Le(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Le(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Le(e,["Enter"],{kind:"model-switch"}),await Ul(800);let a=await $P();return{verified:!!a&&(a.modelId===t.model||a.displayModelId===t.model||r(a.displayName).startsWith(r(t.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function sx(e,t,n){return e==="codex"?IP(t,n):e==="cursor"?RP(t,n):_P(t,n)}var Ul,vP,SP,ex,TP,EP,tx,nx,ax=j(()=>{lt();Qa();Ul=e=>new Promise(t=>setTimeout(t,e)),vP=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,SP=300,ex=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 5"},{alias:"sonnet",label:"Sonnet 5"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],TP=new Set([...ex.map(e=>e.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),EP=[{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.6-terra",label:"GPT-5.6 Terra"},{alias:"gpt-5.6-luna",label:"GPT-5.6 Luna"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}],tx=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),nx=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function Wl(e){let t=e||{},n=OP(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function OP(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:gt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:gt(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:gt(e.agent),payload:{...Er(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:gt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Er(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{change:"subtask-resumed",...Er(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...Er(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Er(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Er(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{...Er(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Er(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:gt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function lx(e,t){let n=Wl(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Up(()=>$(r,n))}async function ux(e){if(!(!e?.id||ix.has(e.id))){ix.add(e.id);try{let t=await F(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=Wl(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(e,o)}}catch{}}}function Er(e){let{type:t,at:n,...r}=e;return r}function dx(e,t,n){!n?.promptId||Fp.get(e.id)===n.promptId||(Fp.set(e.id,n.promptId),Up(()=>$(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:gt(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function Hp(e,t,n,r={}){Fp.delete(e.id),Up(()=>$(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:gt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var gt,Hl,Up,cx,ix,Fp,Wp=j(()=>{me();gt=e=>e||null;Hl=Promise.resolve(),Up=e=>(Hl=Hl.then(e).catch(()=>{}),Hl),cx=()=>Hl;ix=new Set;Fp=new Map});function DP(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function gx(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of DP(n).reverse()){if(!CP.test(r))continue;let o=r.endsWith("?")&&r.length<=PP&&NP.test(r);if(MP.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function bx(e){let t=String(e||""),n=[];BP.test(t)&&n.push("commit"),yx.test(t)&&n.push("rebase"),jP.test(t)&&n.push("push");let r=FP.test(t);!r&&LP.test(t)&&n.push("pr");let o=null;if(r){let s=t.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||t.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function kx(e){return yx.test(String(e||""))}function Os(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",h=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${h}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${h}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}function xx(e,{branch:t=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(e)?e:["commit","push","pr"]),a=t?` on this branch (${t})`:"",i=[];s.has("commit")&&i.push(`commit the work${a} with a clear commit message`),s.has("rebase")&&i.push(`rebase ${s.has("commit")?"the branch":`this branch${t?` (${t})`:""}`} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`),i.push("move the work to a fresh branch (branch off the current HEAD with a clear new name)"),i.push("push the new branch to the remote"),i.push(`open a pull request${n?` against ${n}`:""} from the new branch with a concise title and a description of what changed and why (use \`gh pr create\`)`);let c=[];s.has("commit")||c.push("don't create any new commits \u2014 ship what's already committed"),o&&!s.has("rebase")&&c.push("don't rebase \u2014 leave the branch's history as it is");let l=`Yes \u2014 go ahead, as a NEW pull request: ${i.join(", then ")}.`;return c.length&&(l+=` ${c.join(", and ").replace(/^./,u=>u.toUpperCase())}.`),l+=` Leave the existing open PR${r?` #${r}`:""}${t?` and its remote branch (${t})`:" and its remote branch"} exactly as they are \u2014 don't push ${t||"the old branch"} itself.`,l}function vx(e,t=Date.now()){let n=[],r=null;for(let o of e||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&t-r.lastActivity<=UP&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Sx(e,t){let n=new Set;for(let r of t||[]){if(!r.settled)continue;let o=null;for(let s of e||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to+HP||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Tx(e){let t=(Array.isArray(e)?e:[]).filter(r=>fx[r]).map(r=>fx[r]);return`The ship you were given the go-ahead for${t.length?` (${t.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}var px,mx,hx,wx,CP,MP,NP,PP,Ar,BP,yx,jP,LP,FP,UP,HP,fx,bo=j(()=>{px=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,mx=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,hx=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,wx=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|clean|interactive)\s)rebase(?!s\b)(?:\s+(?:it|this|that|them|(?:this|that|the)\s+branch))?(?:\s+(?:on(?:to)?|against|atop|over)\s+\S+)?/.source,CP=new RegExp(`\\b(?:${px}|${mx}|${hx}|${wx})\\b`,"i"),MP=/\b(?:want\s+me\s+to|would\s+you\s+like(?:\s+me)?(?:\s+to)?|should\s+i|shall\s+i|do\s+you\s+want(?:\s+me)?(?:\s+to)?|happy\s+to|ready\s+for\s+me\s+to|let\s+me\s+know\s+(?:if|when|whether|and)|say\s+the\s+word|give\s+me\s+the\s+(?:word|go[\s-]?ahead)|if\s+you(?:'d|\s+would)?\s+(?:like|want|prefer)|just\s+say\s+(?:so|yes|go))\b/i,NP=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,PP=90;Ar=["commit","rebase","push","pr"],BP=new RegExp(`\\b(?:${px})\\b`,"i"),yx=new RegExp(`\\b(?:${wx})\\b`,"i"),jP=new RegExp(`\\b(?:${mx})\\b`,"i"),LP=new RegExp(`\\b(?:${hx})\\b`,"i"),FP=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;UP=3e4;HP=2500;fx={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import WP from"node:crypto";import ql from"node:fs/promises";import Ex from"node:path";import qP from"node:os";async function He(){return _r||(qp??=ql.readFile(Gp,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(_r=e,!t&&Ne()){let{records:n,source:r}=await et("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;_r[oe(i.repo,i.number)]=i}Object.keys(_r).length&&await Vp().catch(()=>{})}}return _r}).finally(()=>qp=null),qp)}async function Vp(){let e=JSON.stringify({links:_r},null,2),t=`${Gp}.${process.pid}.tmp`;await ql.mkdir(Ax,{recursive:!0}),await ql.writeFile(t,e),await ql.rename(t,Gp)}async function Qe(e){Kp=Kp.then(Vp,Vp),await Kp.catch(()=>{});let t=_r?.[e];t&&wt("prlinks",{id:_x(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Ot({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await He(),c=oe(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:KP.has(a),firstSeen:Date.now()},await Qe(oe(e,t)),{link:i[c],created:!0})}async function Ln(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||n.announced?null:(n.announced=!0,await Qe(oe(e,t)),n)}async function tt(e,t){return!e||t==null?null:(await He())[oe(e,t)]??null}async function Ir(e,t,n){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,delete r.followupPending,await Qe(oe(e,t)),r):null}async function Ix(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[oe(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Qe(oe(e,t)),o):null}async function $x(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[oe(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Qe(oe(e,t)),o):null}async function Kl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Qe(oe(e,t)),r)}async function Gl(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Qe(oe(e,t)),n)}async function Vl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Qe(oe(e,t)),r)}async function zl(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Qe(oe(e,t)),n)}async function Rx(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(r.baseFoldAt=n,await Qe(oe(e,t)),r):null}async function Ox(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Qe(oe(e,t)),n)}async function Cx(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[oe(e,t)];return r?(r.lastHeadSha=n,await Qe(oe(e,t)),r):null}async function Mx(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await He())[oe(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Qe(oe(e,t))),{prev:o,link:r}}async function mi(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Qe(oe(e,t)),r):null}async function ko(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Qe(oe(e,t)),r):null}async function hi(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[oe(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Qe(oe(e,t)),r):null}async function Nx(e,t,n){if(!e||t==null)return null;let r=(await He())[oe(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Qe(oe(e,t)),r}async function Px(e,t,n){if(!e||t==null)return null;let r=(await He())[oe(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.followupPending=[...o.values()],await Qe(oe(e,t)),r}async function wi(e,t){if(!e||t==null)return null;let n=(await He())[oe(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Qe(oe(e,t)),n)}async function Dx(){return Object.values(await He()).filter(e=>!GP.has(e.state))}async function xo(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function Bx(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await He(),r=0;for(let[o,s]of Object.entries(n))await wt("prlinks",{id:_x(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Ax,Gp,oe,_x,_r,qp,Kp,KP,GP,Fn=j(()=>{ur();Ax=process.env.BOSTRAT_STATE_DIR||Ex.join(process.env.HOME||qP.tmpdir(),".bostrat"),Gp=Ex.join(Ax,"pr-links.json"),oe=(e,t)=>`${e}#${Number(t)}`,_x=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=WP.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},_r=null,qp=null,Kp=Promise.resolve();KP=new Set(["api","observed","reconciled"]);GP=new Set(["merged","closed","lost"])});import VP from"node:fs";import zp from"node:fs/promises";import Ux from"node:path";import zP from"node:os";function qx(e,t=!1){if(!t&&e-Lx<YP)return;Lx=e;let n=JSON.stringify({windowStart:Cs,byCaller:Object.fromEntries(Un),lastHour:$r});Fx=Fx.then(()=>zp.mkdir(Hx,{recursive:!0})).then(()=>zp.writeFile(`${Yl}.tmp`,n)).then(()=>zp.rename(`${Yl}.tmp`,Yl)).catch(()=>{})}function Kx(e){e-Cs<Wx||($r={start:Cs,total:Jl(Un),byCaller:Object.fromEntries(Un)},$r.total&&console.log(`[github] ${$r.total} calls last hour`,$r.byCaller),Cs=e,Un=new Map,qx(e,!0))}function Hn(e="other",t=Date.now()){Kx(t),Un.set(e,(Un.get(e)||0)+1),qx(t)}function Gx(e=Date.now()){return Kx(e),{windowStart:Cs,total:Jl(Un),byCaller:Object.fromEntries(Un),lastHour:$r,backoffUntil:vo>e?vo:null}}function Rr(e){return JP.test(String(e?.message||e||""))}function Or(e,t=Date.now()){return vo<=t&&console.warn(`[github] rate limited \u2014 backing off ${jx/6e4} min:`,String(e?.message||e||"").slice(0,200)),vo=Math.max(vo,t+jx),vo}function Xl(e=Date.now()){return Math.max(0,vo-e)}var Hx,Yl,Wx,jx,YP,Cs,Un,$r,vo,Lx,Jl,Fx,JP,gi=j(()=>{Hx=process.env.BOSTRAT_STATE_DIR||Ux.join(process.env.HOME||zP.tmpdir(),".bostrat"),Yl=Ux.join(Hx,"github-metrics.json"),Wx=60*6e4,jx=5*6e4,YP=6e4,Cs=Date.now(),Un=new Map,$r=null,vo=0,Lx=0,Jl=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(VP.readFileSync(Yl,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Wx?(Cs=e.windowStart,Un=new Map(Object.entries(e.byCaller||{})),$r=e.lastHour||null):Jl(new Map(Object.entries(e.byCaller||{})))&&($r={start:e.windowStart,total:Jl(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Fx=Promise.resolve();JP=/rate limit|too quickly|abuse detection|HTTP 429/i});import Vx from"node:fs/promises";import zx from"node:path";import XP from"node:os";function Ql({change:e,repo:t=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:e,repo:t,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Zl.push(l),Zl.length>QP&&Zl.shift();let u=Yp.get(e)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),Yp.set(e,u),eD(l),l}async function eD(e){try{await Vx.mkdir(Yx,{recursive:!0}),await Vx.appendFile(ZP,JSON.stringify(e)+`
|
|
179
|
+
`)}catch{}}function Jx(){let e={};for(let[t,n]of Yp)e[t]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:e,recent:Zl.slice(-20)}}var Yx,ZP,QP,Zl,Yp,eu=j(()=>{Yx=process.env.BOSTRAT_STATE_DIR||zx.join(process.env.HOME||XP.tmpdir(),".bostrat"),ZP=zx.join(Yx,"github-latency.ndjson"),QP=100,Zl=[],Yp=new Map});import{execFile as tD}from"node:child_process";import{promisify as nD}from"node:util";async function rD(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ns("git",["-C",e,"rev-parse","HEAD"]),Ns("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await $({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function Ut(e,t,n={}){let{caller:r="prs",...o}=n,s=await ln({cwd:e});Hn(r);try{let{stdout:a}=await Ns("gh",t,{cwd:e,...oD,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Rr(a)&&Or(a),a}}async function Zp(){if(tu&&Date.now()-tu.at<6e4)return tu.data;let e;try{Hn("auth");let t=await Ns("gh",["auth","status"],{timeout:1e4});e={installed:!0,authenticated:!0,login:`${t.stdout||""}${t.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(t){e=t?.code==="ENOENT"||t?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(e.installed&&!e.authenticated)try{let t=await _a();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return tu={at:Date.now(),data:e},e}async function sD(e){try{let{stdout:t}=await Ns("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Zx(e){let t=e.name||e.context||"check",n=e.detailsUrl||e.targetUrl||null,r=e.completedAt||e.createdAt||null,o;return e.__typename==="StatusContext"?o=e.state==="SUCCESS"?"pass":e.state==="PENDING"||e.state==="EXPECTED"?"pending":"fail":e.status&&e.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(e.conclusion)?o="pass":o="fail",{name:t,state:o,url:n,completedAt:r}}function Jp(e){let t=(e||[]).map(Zx),n=t.filter(s=>s.state==="pending").length,r=t.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=t.length&&!n&&r.length?Math.max(...r):null;return{total:t.length,passed:t.filter(s=>s.state==="pass").length,failed:t.filter(s=>s.state==="fail").length,pending:n,failing:t.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}function em(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function ev(e,t){if(!t)return null;let n=await Se(e);if(!n)return null;try{let r=await Ut(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function tv(e){return(e||[]).map(t=>({name:t.name,detailsUrl:t.html_url||null,status:String(t.status||"").toUpperCase(),conclusion:t.conclusion?String(t.conclusion).toUpperCase():null,completedAt:t.status==="completed"&&t.updated_at||null}))}async function nv(e,t){let n=await ev(e,t);return n===null?{...Jp([]),unavailable:!0}:Jp(tv(n))}function nu(e,t=Jp(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function iD(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Ms.delete(s);continue}r.add(s),Ms.has(s)||Ms.set(s,n),o.mergeabilityPending=n-Ms.get(s)<aD}for(let o of Ms.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Ms.delete(o);return t}async function Ps(e,t){let n=await Wn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Ut(n.path,["pr","view",String(r),"--json",Qp]);return nu(JSON.parse(o))}catch(o){if(!em(o))throw o;let s=await Ut(n.path,["pr","view",String(r),"--json",Qx]),a=JSON.parse(s);return nu(a,await nv(n.path,a.headRefOid))}}async function hn(e,{caller:t="prs"}={}){try{let n=await Ut(e,["pr","list","--json",Qp,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>nu(r)).sort(Xx)}catch(n){if(!em(n))throw n;let r=await Ut(e,["pr","list","--json",Qx,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>nu(o,await nv(e,o.headRefOid))))).sort(Xx)}}function gn(){yi.clear()}async function uD(e,t){if(!e)return t;for(let n of t){let r=await tt(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Kl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Vl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function rv(){let e=await Zp();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await On())await sD(a.path)&&t.push(a);let n=Date.now(),r=Xl(n)>0,o=await Promise.all(t.map(async a=>{let i=yi.get(a.path),c=i?.pending?lD:cD;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Se(a.path).catch(()=>null),u=iD(a.path,await hn(a.path,{caller:"tab"}),n);if(await uD(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Ql({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return yi.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:wn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of yi.keys())s.has(a)||yi.delete(a);return{auth:e,repos:o}}async function Wn(e){let t=(await On()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function So(e){return Zo(e)}async function Ds(e,t,{method:n="squash"}={}){let r=await Wn(e);if(!bi.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(t);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await ln({cwd:r.path})).GH_TOKEN){let a=await Hd().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ut(r.path,s)}catch(a){throw new Error(wn(a))}finally{gn()}return{ok:!0,number:o}}async function ki(e,t){let n=await Wn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ut(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(wn(s))}finally{gn()}let o=await Se(n.path).catch(()=>null);return o&&await Rx(o,r).catch(()=>{}),{ok:!0,number:r}}async function tm(e,t){let n=await Wn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ut(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(wn(o))}finally{gn()}return{ok:!0,number:r}}function nm(e){return e.green&&!e.isDraft}function mD(e){return e.isDraft?"draft":e.mergeable==="CONFLICTING"?`conflicts with ${e.baseRefName}`:e.checks.failed>0?"checks failing":e.checks.pending>0?"checks running":e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":e.mergeStateStatus==="BEHIND"?`behind ${e.baseRefName}`:e.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${e.mergeStateStatus})`}function ov(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:nm(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function sv(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function xi(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function av(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(xi))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function iv(){let e={};for(let[t,n]of Xp)e[t]={...n};return e}async function cv(e,{method:t="merge",onMerged:n=null}={}){let r=await Wn(e);if(!bi.has(t))throw new Error(`invalid merge method: ${t}`);if(Xp.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await hn(r.path)).filter(nm).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:t,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return Xp.set(r.path,s),wD(s,o,{listPrs:()=>hn(r.path),merge:a=>Ds(r.path,a,{method:t}),sleep:pD,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function hD(e,t){let n=Date.now()+(t.settleMaxMs??fD),r=null;for(;;){let o=null;try{if(o=await t.listPrs(),r=null,!o.some(a=>e.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await t.sleep(dD)}}async function wD(e,t,n){try{let r=t;for(let o=0;o<t.length;o++){let s=t[o];if(e.current=s.number,o>0)try{r=await hD(t.slice(o).map(i=>i.number),n)}catch(i){for(let c of t.slice(o))e.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${wn(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){e.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!nm(a)){e.skipped.push({number:a.number,title:a.title,reason:mD(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:wn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),gn()}}async function rm(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ns("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${wn(l)}`)}await rD(e);let o=n||"";if((await ln({cwd:e})).GH_TOKEN){let l=await Hd().catch(()=>null);l&&(o=`${o?`${o}
|
|
180
|
+
|
|
181
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await Ut(e,s)}catch(l){throw new Error(wn(l))}finally{gn()}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 lv(e,t){let n=await Wn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Ut(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(wn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!em(i))throw i;let c=await Ut(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await ev(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(wn(i));o=tv(l).map(Zx).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=gD(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Ut(n.path,["run","view",c,"--log-failed"]).then(u=>yD(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function gD(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function yD(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
182
182
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
183
183
|
`+o.slice(-t).join(`
|
|
184
184
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
185
|
-
`+r.slice(-n)),r}function
|
|
186
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
187
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+uD(a)+"[ \\t]*$","m").exec(t.slice(i+1));if(!l)continue;let u=i+1+l.index+l[0].length;t=t.slice(0,i)+t.slice(u)}return t}function Xp(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function av(e){let t=ov(e),n=t.match(iD);if(!n)return null;let r=Xp(t,n.index);return lD.test(r)?null:{repo:r.match(sv)?.[1]??null,head:r.match(dD)?.[1]??null}}function iv(e){let t=ov(e),n=t.match(cD);if(!n)return null;let r=Xp(t,n.index),o=Xp(t,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){pD.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(sv)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function hD(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Bn("observer");let{stdout:c}=await ev("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",mD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Tr(l)&&Ar(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-oD&&!n(c)),s=await tn({slug:e}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function wD(e,t){if(t)return[t];let n=[];for(let r of cv(e)){let o=await xe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await En().catch(()=>[])){let o=await xe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function cv(e){let t=e.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.base).filter(Boolean):[t?.base||e.cwd].filter(Boolean)}async function gD(e,t,n){let r=await Os(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||fD(c.headRefName,e.id),i=[];for(let c of await wD(e,t?.repo??null)){let l;try{l=await hD(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await Qe(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function lv(e,t,{delays:n=tv,turnEnd:r}={}){let o=av(t.command);if(!o)return null;let s=!!iv(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Er.has(a))return null;Er.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await gD(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:m}=await St({repo:d,number:f.number,trackId:e.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(p?.trackId===e.id&&await Nn(d,f.number))?null:(await Yp(t.box,t.agent,r),await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{Er.delete(a)}})();return c.catch(()=>Er.delete(a)),c}function uv(e,t,{delays:n=tv,turnEnd:r,openedWait:o}={}){let s=iv(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Er.has(a))return null;Er.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let d=[];if(c)d=[c];else{let m=cv(e);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await xe(w).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await Os(e).catch(()=>[])).filter(m=>m!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(m,w)=>{let y=await Qe(c,m.number);return y&&y.trackId!==e.id?null:(y||await St({repo:c,number:m.number,trackId:e.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await ci(c,m.number),w&&((await F(e).catch(()=>[])).some(I=>I.type==="pr.state.changed"&&(I.payload?.change==="auto-armed"||I.payload?.change==="merged")&&I.payload?.number===m.number)||(await Yp(t.box,t.agent,r),await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(S=>setTimeout(S,m));let w=[];for(let S of d)try{Bn("observer");let{stdout:A}=await ev("gh",["pr","view",String(l),"--repo",S,"--json",yD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await tn({slug:S})}}),R=JSON.parse(A||"null");R?.number&&w.push({slug:S,pr:R})}catch(A){Tr(A)&&Ar(A)}let y=w.find(S=>S.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let x=y.pr;if(!x?.number)continue;if(x.state==="CLOSED")return null;if(x.state!=="MERGED"){if(p=x,s.auto)return f(x,!0);continue}let T=await Qe(c,x.number);if(T&&T.trackId!==e.id)return null;T||await St({repo:c,number:x.number,trackId:e.id,url:x.url,headRef:x.headRefName,title:x.title,source:"merge-observed"}),await Yp(t.box,t.agent,r);let I=await F(e).catch(()=>[]);if(I.some(S=>S.type==="pr.state.changed"&&S.payload?.change==="merged"&&S.payload?.number===x.number))return null;let $=S=>S.some(A=>A.type==="pr.state.changed"&&A.payload?.change==="opened"&&A.payload?.number===x.number),D=0;if(!$(I)){if(av(t.command)&&await Nn(c,x.number)){let S=Date.now();D=S+1,await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:S,change:"opened",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c,selfMerge:!0}})}else if((await Qe(c,x.number))?.announced){let S=o?.capMs??sD,A=o?.pollMs??aD,R=Date.now()+S;for(;Date.now()<R&&(await new Promise(E=>setTimeout(E,A)),!$(await F(e).catch(()=>[]))););}}return await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),D),change:"merged",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c}}),await Fn(c,x.number).catch(()=>{}),await Ln(c,x.number).catch(()=>{}),await vr(c,x.number,"merged"),x}return p?f(p,!1):null}finally{Er.delete(a)}})();return i.catch(()=>Er.delete(a)),i}var ev,tv,nD,rD,oD,sD,aD,nv,rv,iD,cD,lD,Qx,uD,sv,dD,fD,pD,mD,Er,yD,dv=j(()=>{ue();tr();it();di();Pn();_r();gi();Cs();ev=tD(eD),tv=[4e3,15e3,45e3],nD=2500,rD=5*6e4;oD=15e3,sD=9e4,aD=2e3,nv=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,rv=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,iD=new RegExp(`${nv}${rv}create\\b`),cD=new RegExp(`${nv}${rv}merge\\b`),lD=/\s--web\b/,Qx=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,uD=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");sv=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,dD=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,fD=Zx;pD=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);mD="number,url,title,headRefName,createdAt";Er=new Set;yD="number,url,title,headRefName,state"});import bD from"node:fs/promises";import Qp from"node:path";import kD from"node:os";function Zp(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Zl(e){let t=xD.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Zp(n,"from"),s=o?o.replace(/^uds:/,""):null,a=fv(s);return{socket:s,pid:a,name:Zp(n,"from-name"),mode:Zp(n,"from-mode"),text:r.trim()}}function fv(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function vD(){return process.env.CLAUDE_CONFIG_DIR?Qp.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Qp.join(kD.homedir(),".claude","sessions")}async function SD(e){let t=fv(e);if(!t)return null;try{let n=await bD.readFile(Qp.join(vD(),`${t}.json`),"utf8"),r=JSON.parse(n);return{pid:t,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function pv(e,t){let n=await SD(e?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof t=="function")try{o=await t(n?.sessionId??null,r)??null}catch{o=null}return{socket:e?.socket??null,pid:n?.pid??e?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??e?.name??null,mode:e?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}var xD,em=j(()=>{xD=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function tm(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function nm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!tm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),mv);try{let a=await hv(`${$e(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function wv(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!tm(s))return{ok:!1,source:"unconfigured"};let i=Ql.get(e.id)||0;if(a()-i<TD)return{ok:!1,source:"debounced"};Ql.set(e.id,a());let c=await yi({env:s}),l=c.loops.find(f=>f.trackId===e.id);if(!l)return Ql.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),d=await nm({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||Ql.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function yi({env:e=process.env}={}){if(!tm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),mv);try{let r=await hv(`${$e(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var mv,hv,TD,Ql,rm=j(()=>{it();mv=1e4,hv=(...e)=>fetch(...e);TD=10*6e4,Ql=new Map});import{randomUUID as AD}from"node:crypto";function bi(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function gv(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function yv(e){return`job|${e.box}|id:${e.id}`}async function sm(e){return om.has(e.id)?om.get(e.id):(eu.has(e.id)||eu.set(e.id,F(e).then(t=>{let n={keys:new Set,cmdN:0};for(let r of t)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(bi({role:o,sourceId:r.payload.sourceId})),n.keys.add(bi({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(bi({role:"user",sourceId:r.payload.sourceId})),n.keys.add(bi({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(gv({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(yv({id:r.payload.id,box:r.boxId}));return om.set(e.id,n),n}).finally(()=>eu.delete(e.id))),eu.get(e.id))}async function bv(e,t){let n=new Set;try{let r=(await sm(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=bi(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?Zl(o.text):null;if(a){await O(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:a.text,ts:o.ts,keyText:String(o.text||"").slice(0,40),from:await pv(a,ki),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=eb(o);if(i){await O(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),wv(e,{provider:o.agent||null,cls:i.cls,status:i.status,text:o.text}).catch(()=>{});continue}if(o.role!=="user"&&o.box&&n.add(o.box),await O(e,{type:o.role==="user"?"turn.user":"turn.agent",actor:o.role==="user"?"user":"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:o.text,ts:o.ts,model:o.model||null,tools:o.tools||void 0,boxRole:o.boxRole,sourceId:o.sourceId||void 0,note:o.note||void 0,images:o.images?.length?o.images:void 0}}),o.role==="assistant"&&o.box===e.leadBox&&e.workspace?.branch){let c=tx(o.text);if(c){let l=ox(c),u;if(e.workspace?.kind==="multi"){let m=await Ms(e).catch(()=>[]);u=m.length?m.map(w=>({name:w.name,slug:w.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await Rs(e.cwd).catch(()=>null))||e.workspace.branch,f=await Vx(e,{branch:d,rebase:l}).catch(()=>null),p=f?null:rx(c);if(f&&f.available.length===0)continue;await O(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:AD(),ts:o.ts,excerpt:c,branch:d,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:f?.available,defaults:f?.defaults,prState:f?.prState,openPr:f?.openPr||void 0,repos:f?.repos||u,offered:f?.defaults||p?.offered||void 0,toPr:p?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function kv(e,t){try{let n=await sm(e),r=[...t||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=gv(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await O(e,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),lv(e,o),uv(e,o))}}catch{}}async function xv(e,t){try{let n=await sm(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=yv(o);n.keys.has(s)||(n.keys.add(s),await O(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var om,eu,vv=j(()=>{ue();Ss();zx();dv();Cs();em();_r();rl();rm();om=new Map,eu=new Map});function ED(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
`)
|
|
191
|
-
`)){
|
|
185
|
+
`+r.slice(-n)),r}function wn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
186
|
+
`).find(n=>n.trim())||"gh failed"}var Ns,oD,tu,Qp,Qx,aD,Ms,Xx,cD,lD,yi,bi,dD,fD,pD,Xp,Bs=j(()=>{ar();it();Fn();gi();eu();me();Ns=nD(tD),oD={timeout:3e4,maxBuffer:8*1024*1024};tu=null;Qp="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Qx=Qp.replace("statusCheckRollup,","");aD=9e4,Ms=new Map;Xx=(e,t)=>e.number-t.number;cD=3e4,lD=1500,yi=new Map;bi=new Set(["squash","merge","rebase"]);dD=2500,fD=9e4,pD=e=>new Promise(t=>setTimeout(t,e));Xp=new Map});import{execFile as bD}from"node:child_process";import{promisify as kD}from"node:util";async function vD(e,t){try{let{stdout:n}=await xD("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function SD(e,t,n,{rebase:r=!1,listPrs:o=hn}={}){let s=await ui(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await di(e.dir,n).catch(()=>0),i=t?await vD(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let f=s||a>0,d=[];s&&d.push("commit"),r&&d.push("rebase"),(s||c)&&d.push("push"),f&&(u!=="open"||s||c)&&d.push("pr");let p=d.filter(h=>!(h==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:ru.filter(h=>d.includes(h)),defaults:ru.filter(h=>p.includes(h))}}async function ou(e,{branch:t,rebase:n=!1,listPrs:r=hn}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(d=>d?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await Es(s[0].dir).catch(()=>!1))return null;let a=[];for(let d of s)a.push(await SD(d,t,d.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(d=>d.dirty||d.aheadBase>0||d.openPr),c=i.length?i:a,l=ru.filter(d=>c.some(p=>p.available.includes(d))),u=ru.filter(d=>c.some(p=>p.defaults.includes(d))),f=c.filter(d=>d.openPr);return{available:l,defaults:u,prState:f.length?"open":c.every(d=>d.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(d=>({name:d.name,slug:d.slug,dirty:d.dirty,unpushed:d.unpushed,prState:d.prState,openPr:d.openPr}))}}var xD,ru,om=j(()=>{Pl();Bs();xD=kD(bD),ru=["commit","rebase","push","pr"]});function su(e,t,n){let{state:r,reason:o}=Nf(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of vi)a-c.at>=uv()&&vi.delete(i);vi.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=vi.get(e);return s?Date.now()-s.at>=uv()?(vi.delete(e),!1):s.busy:!1}async function sm(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Ce(e,{lines:Xe}).catch(()=>"");if(!su(e,t,r))return Pb(t,r)?{working:!0,prompted:!1}:n;if(wo(t,r)){let o=await Nn(e).catch(()=>r),s=Tr({name:e,agent:t},o);if(s)return!Ml({name:e,agent:t},s,o)||C0(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function To(e,t){return(await sm(e,t)).working}var uv,vi,Si=j(()=>{lt();Za();Ap();Ip();uv=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,vi=new Map});import{execFile as TD}from"node:child_process";import{promisify as ED}from"node:util";async function Cr(e){if(!e)return null;let t=dv.get(e);if(t&&Date.now()-t.at<_D)return t.branch;let n=null;try{let{stdout:r}=await AD("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return dv.set(e,{at:Date.now(),branch:n}),n}function fv(e){return String(e||"").match(ID)?.[1]??null}function $D(e,t){let n=fv(e);return!!n&&!!t&&n===t}function pv(e,t){let n=fv(e);return!!n&&n!==t}function RD(e){let t=e?.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.dir).filter(Boolean):[t?.dir||e?.cwd].filter(Boolean)}async function js(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of RD(e))n(await Cr(r));return t}async function au(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=>$D(t,a.id));if(s)return s;for(let a of r)if(!Ve(a)&&(await js(a)).includes(t))return a;return null}var AD,_D,dv,ID,Eo=j(()=>{Bn();AD=ED(TD),_D=5e3,dv=new Map;ID=/^track\/.+-([0-9a-f]{8})$/});import{execFile as OD}from"node:child_process";import{promisify as CD}from"node:util";async function am(e,t,{midTurn:n=To,pollMs:r=MD,capMs:o=ND}={}){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 bv(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){mv.lastIndex=n;let o=mv.exec(t);if(!o)break;n=o.index+o[0].length;let s=o[1]==="-",a=o[2]??o[3]??o[4],i=t.indexOf(`
|
|
187
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+UD(a)+"[ \\t]*$","m").exec(t.slice(i+1));if(!l)continue;let u=i+1+l.index+l[0].length;t=t.slice(0,i)+t.slice(u)}return t}function im(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function xv(e){let t=bv(e),n=t.match(jD);if(!n)return null;let r=im(t,n.index);return FD.test(r)?null:{repo:r.match(kv)?.[1]??null,head:r.match(HD)?.[1]??null}}function vv(e){let t=bv(e),n=t.match(LD);if(!n)return null;let r=im(t,n.index),o=im(t,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){qD.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(kv)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function GD(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Hn("observer");let{stdout:c}=await hv("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",KD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Rr(l)&&Or(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-PD&&!n(c)),s=await ln({slug:e}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function VD(e,t){if(t)return[t];let n=[];for(let r of Sv(e)){let o=await Se(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await On().catch(()=>[])){let o=await Se(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Sv(e){let t=e.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.base).filter(Boolean):[t?.base||e.cwd].filter(Boolean)}async function zD(e,t,n){let r=await js(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||WD(c.headRefName,e.id),i=[];for(let c of await VD(e,t?.repo??null)){let l;try{l=await GD(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await tt(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===e.id&&f.announced)return{settled:!0};if(f&&f.trackId!==e.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==e.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function Tv(e,t,{delays:n=wv,turnEnd:r}={}){let o=xv(t.command);if(!o)return null;let s=!!vv(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Mr.has(a))return null;Mr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await zD(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:h}=await Ot({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!h&&!(p?.trackId===e.id&&await Ln(f,d.number))?null:(await am(t.box,t.agent,r),await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{Mr.delete(a)}})();return c.catch(()=>Mr.delete(a)),c}function Ev(e,t,{delays:n=wv,turnEnd:r,openedWait:o}={}){let s=vv(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Mr.has(a))return null;Mr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let h=Sv(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let g=await Se(w).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=(await js(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(h,w)=>{let g=await tt(c,h.number);return g&&g.trackId!==e.id?null:(g||await Ot({repo:c,number:h.number,trackId:e.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await mi(c,h.number),w&&((await F(e).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===h.number)||(await am(t.box,t.agent,r),await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},p=null;for(let h of n){await new Promise(T=>setTimeout(T,h));let w=[];for(let T of f)try{Hn("observer");let{stdout:_}=await hv("gh",["pr","view",String(l),"--repo",T,"--json",YD],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await ln({slug:T})}}),O=JSON.parse(_||"null");O?.number&&w.push({slug:T,pr:O})}catch(_){Rr(_)&&Or(_)}let g=w.find(T=>T.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!g)continue;c=g.slug;let k=g.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return d(k,!0);continue}let S=await tt(c,k.number);if(S&&S.trackId!==e.id)return null;S||await Ot({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await am(t.box,t.agent,r);let E=await F(e).catch(()=>[]);if(E.some(T=>T.type==="pr.state.changed"&&T.payload?.change==="merged"&&T.payload?.number===k.number))return null;let R=T=>T.some(_=>_.type==="pr.state.changed"&&_.payload?.change==="opened"&&_.payload?.number===k.number),B=0;if(!R(E)){if(xv(t.command)&&await Ln(c,k.number)){let T=Date.now();B=T+1,await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:T,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await tt(c,k.number))?.announced){let T=o?.capMs??DD,_=o?.pollMs??BD,O=Date.now()+T;for(;Date.now()<O&&(await new Promise(I=>setTimeout(I,_)),!R(await F(e).catch(()=>[]))););}}return await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),B),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Kn(c,k.number).catch(()=>{}),await qn(c,k.number).catch(()=>{}),await Ir(c,k.number,"merged"),k}return p?d(p,!1):null}finally{Mr.delete(a)}})();return i.catch(()=>Mr.delete(a)),i}var hv,wv,MD,ND,PD,DD,BD,gv,yv,jD,LD,FD,mv,UD,kv,HD,WD,qD,KD,Mr,YD,Av=j(()=>{me();ar();it();gi();Fn();Nr();Si();Eo();hv=CD(OD),wv=[4e3,15e3,45e3],MD=2500,ND=5*6e4;PD=15e3,DD=9e4,BD=2e3,gv=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,yv=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,jD=new RegExp(`${gv}${yv}create\\b`),LD=new RegExp(`${gv}${yv}merge\\b`),FD=/\s--web\b/,mv=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,UD=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");kv=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,HD=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,WD=pv;qD=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);KD="number,url,title,headRefName,createdAt";Mr=new Set;YD="number,url,title,headRefName,state"});import JD from"node:fs/promises";import lm from"node:path";import XD from"node:os";function cm(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function iu(e){let t=ZD.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=cm(n,"from"),s=o?o.replace(/^uds:/,""):null,a=_v(s);return{socket:s,pid:a,name:cm(n,"from-name"),mode:cm(n,"from-mode"),text:r.trim()}}function _v(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function QD(){return process.env.CLAUDE_CONFIG_DIR?lm.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):lm.join(XD.homedir(),".claude","sessions")}async function eB(e){let t=_v(e);if(!t)return null;try{let n=await JD.readFile(lm.join(QD(),`${t}.json`),"utf8"),r=JSON.parse(n);return{pid:t,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function Iv(e,t){let n=await eB(e?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof t=="function")try{o=await t(n?.sessionId??null,r)??null}catch{o=null}return{socket:e?.socket??null,pid:n?.pid??e?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??e?.name??null,mode:e?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}var ZD,um=j(()=>{ZD=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function dm(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function fm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!dm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),$v);try{let a=await Rv(`${Ie(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function Ov(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!dm(s))return{ok:!1,source:"unconfigured"};let i=cu.get(e.id)||0;if(a()-i<tB)return{ok:!1,source:"debounced"};cu.set(e.id,a());let c=await Ti({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return cu.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),f=await fm({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||cu.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function Ti({env:e=process.env}={}){if(!dm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),$v);try{let r=await Rv(`${Ie(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var $v,Rv,tB,cu,pm=j(()=>{it();$v=1e4,Rv=(...e)=>fetch(...e);tB=10*6e4,cu=new Map});function Cv(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Mv(e,t,n=mm){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await rB(`${Ie(t)}${e}`,{headers:oB(t),signal:r.signal})}finally{clearTimeout(o)}}async function Ei({env:e=process.env,force:t=!1,timeoutMs:n=mm}={}){if(!Cv(e))return{followups:[],source:"unconfigured"};if(!t&&Ao&&Date.now()-Ao.at<nB)return{followups:Ao.followups,source:"cache"};try{let r=await Mv("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return Ao={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Ao?{followups:Ao.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function Nv(e,{env:t=process.env,timeoutMs:n=mm}={}){if(!e)return{followup:null,source:"unconfigured"};if(!Cv(t))return{followup:null,source:"unconfigured"};try{let r=await Mv(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=Ao?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var nB,mm,Ao,rB,oB,Pv=j(()=>{it();nB=6e4,mm=5e3,Ao=null,rB=(...e)=>fetch(...e);oB=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import{randomUUID as Fs,createHash as sB}from"node:crypto";function Lv(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function uB(e){for(let[t,n]of lB)if(t.test(String(e||"")))return`a standing prompt must not embed this Track's specifics (${n}) \u2014 the card supplies them at fire time; arm a one-shot with \`offer\` instead`;return null}function Fv(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Ls(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>Dv)throw new Error(`label must be \u2264${Dv} characters`);let o=Ls(n.trigger)||(n.ship?"now":""),s=t?iB:jv.filter(u=>u!=="track.opened");if(!s.includes(o))throw new Error(`trigger must be one of ${s.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let a=n.ship&&o==="now"?fB(n.ship):null,i=Ls(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>Bv)throw new Error(`prompt must be \u2264${Bv} bytes`);if(t){let u=uB(i);if(u)throw new Error(u)}let c=Ls(n.why).slice(0,aB)||null,l=t?dB(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function dB(e){if(e==null||e===""||e==="*")return null;let t=String(e).trim();if(!/^[\w.-]+\/[\w.-]+$/.test(t))throw new Error(`repo must be a forge slug like owner/name, got "${e}"`);return t}function fB(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):hm.slice();for(let i of n)if(!hm.includes(i))throw new Error(`ship.actions may only contain ${hm.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=Ls(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>Ls(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function lu(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}async function Uv(e,t,{pull:n=Ei,ship:r={}}={}){if(ym(e))throw new Error("follow-ups don't fire on a loop's run Track \u2014 report the outcome with `bostrat-loop report` instead");let o=Fv(t);if(o.ship)return{kind:"ship",...await hB(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null};if(o.trigger==="now"){let g=Fs(),k=Fs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:g,trigger:"now",label:o.label,prompt:o.prompt,why:o.why}}),await $(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:k,trigger:"now",repo:e.workspace?.repo||null,context:{},rows:[{armId:g,label:o.label,prompt:o.prompt,promptHash:wm(o.prompt),origin:"agent",why:o.why}]}}),{kind:"now",armId:g,offerId:k,...o}}let c=e.workspace?.repo||null,{followups:l}=await n().catch(()=>({followups:[]})),u=Lv(o.label),f=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(f)return{kind:"covered",covered:{id:f.id,label:f.label},...o};let d=await F(s),p=lu(d,{trigger:o.trigger}),h=p.length?p[p.length-1].armId:null,w=Fs();return await $(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:w,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...h?{replaces:h}:{}}}),{kind:"armed",armId:w,replaces:h,...o}}async function Hv(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await F(n);if(!lu(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await $(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function Wv(e,t,{pull:n=Ei}={}){if(ym(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=Fv(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await F(s)).reduce((g,k)=>k.type==="followup.create.offered"?k.payload?.proposalId??g:k.type==="followup.create.answered"&&k.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=Lv(r.label),f=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),d=f.find(g=>g.status==="deleted"),p=f.find(g=>g.status!=="deleted");if(p)throw new Error(`"${p.label}" (${gm[p.trigger]||p.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(d){let g=d.deletedAt?new Date(d.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${d.label}" on ${g} \u2014 do not re-propose it`)}let h=Fs(),w=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await $(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:w?.agent??null,payload:{ts:Date.now(),proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:h,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function qv(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await F(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await $(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await $(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Ai(e,t,n={},{pull:r=Ei}={}){if(!e||ym(e))return null;let o={id:e.id,cwd:e.cwd},s=await F(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null,i=lu(s,{trigger:t}).map(d=>({armId:d.armId,label:d.label,prompt:d.prompt,promptHash:wm(d.prompt),origin:"agent",why:d.why??null})),c=[];if(t!=="now"){let{followups:d}=await r().catch(()=>({followups:[]}));c=d.filter(p=>p.status==="active"&&p.mode!=="off"&&p.trigger===t&&(p.repo==null||p.repo===a)).sort((p,h)=>{let w=g=>g.stats?.offered?(g.stats.accepted||0)/g.stats.offered:0;return w(h)-w(p)||(p.origin==="operator"?-1:0)-(h.origin==="operator"?-1:0)||(h.createdAt||0)-(p.createdAt||0)}).map(p=>({followupId:p.id,label:p.label,promptPreview:pB(p.prompt),promptHash:wm(p.prompt),origin:p.origin,repo:p.repo??null}))}let l=[...i,...c].slice(0,cB);if(!l.length)return null;let u=Fs(),f={ts:Date.now(),offerId:u,trigger:t,repo:a,context:{...n,repo:a},rows:l};return await $(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:f}),f}function mB(e){let t=e?.context||{},n=Array.isArray(t.prs)&&t.prs.length?t.prs:t.prNumber?[{number:t.prNumber,title:t.prTitle,url:t.prUrl,repo:t.repo}]:[],r=o=>`PR #${o.number}${o.title?` "${o.title}"`:""}${o.url?` (${o.url})`:""}${o.repo?` on ${o.repo}`:""}`;switch(e?.trigger){case"pr.merged":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} merged:`:"Follow-up the operator tapped after this Track's PR merged:";case"pr.opened":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} opened:`:"Follow-up the operator tapped after this Track's PR opened:";case"checks.green":return n.length?`Follow-up the operator tapped after CI went green on ${n.map(r).join(" and ")}:`:"Follow-up the operator tapped after CI went green:";case"ship.done":return"Follow-up the operator tapped after the ship you just ran completed:";default:return"Follow-up the operator tapped:"}}async function Kv(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=Nv}=s;if(!t)throw new Error("offerId required");let l={id:e.id,cwd:e.cwd},u=await F(l),f=u.find(E=>E.type==="followup.offered"&&E.payload?.offerId===t);if(!f)throw new Error("unknown offer");if(u.some(E=>E.type==="followup.answered"&&E.payload?.offerId===t))throw new Error("offer already answered");let d=f.payload.rows||[],p=E=>$(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...E}});if(!n&&!r)return await p({action:"dismiss",followupIds:d.map(E=>E.followupId).filter(Boolean),armIds:d.map(E=>E.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let h=null,w=null;if(n){if(!d.some(B=>B.followupId===n))throw new Error("that follow-up is not on this card");let{followup:E,source:R}=await c(n);if(R==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!E||E.status!=="active"){await p({action:"removed",followupId:n});let B=new Error("removed in Settings since it was offered");throw B.code="removed",B}h=E.prompt,w=E.label}else{let E=d.find(B=>B.armId===r);if(!E)throw new Error("that arm is not on this card");if(h=u.find(B=>B.type==="followup.armed"&&B.payload?.armId===r)?.payload?.prompt??E.prompt,w=E.label,!h)throw new Error("the arm's prompt is missing from the log")}let g=!1;if(e.archivedAt){if(!i)throw new Error("this Track is archived");await i(e.id,{freshSession:!0}),g=!0}if(!a)throw new Error("sayToLead unavailable");let k=`${mB(f.payload)}
|
|
188
|
+
|
|
189
|
+
${h}`,S=await a(e.id,k,{kind:"followup-go"});return await p({action:"sent",...n?{followupId:n}:{armId:r},label:w,...o?{promoted:String(o)}:{},...g?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:g,queued:!!S?.queued}}async function hB(e,t,{changedRepos:n=null,probe:r=ou,branchOf:o=Cr}={}){let s=t.ship;if(!e.workspace?.branch)throw new Error("this Track has no branch to ship \u2014 a ship offer needs a worktree Track");let a={id:e.id,cwd:e.cwd},i=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,c=e.workspace?.kind==="multi",l;if(c&&n){let g=await n(e).catch(()=>[]),k=s.repos?g.filter(S=>s.repos.includes(S.name)):g;l=k.length?k.map(S=>({name:S.name,slug:S.repo??null})):void 0}let u=(c?null:await o(e.cwd).catch(()=>null))||e.workspace.branch,f=s.rebase==="offered",d=await r(e,{branch:u,rebase:f}).catch(()=>null),p=[...f?["rebase"]:[],...s.actions].filter(g=>Ar.includes(g));if(d&&d.available.length===0)return{skipped:!0,reason:"nothing to ship \u2014 the tree is clean, pushed, and its PR is open",shipState:d};let h=Fs(),w=t.why?`${t.label} \u2014 ${t.why}`:t.label;return await $(a,{type:"pr.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:i?.agent??null,payload:{offerId:h,ts:Date.now(),excerpt:w,via:"cli",branch:u,base:s.base||e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:"lead",available:d?.available,defaults:d?d.defaults.filter(g=>p.includes(g)||g==="rebase"):void 0,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||l,offered:d?p.filter(g=>d.available.includes(g)):p,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:h,branch:u,offered:p,available:d?.available??null,prState:d?.prState??null}}function Gv(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 Vv(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 zv(e,{pull:t=Ei}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await F({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?lu(s):[]}}var Dv,Bv,aB,jv,iB,gm,cB,hm,wm,Ls,lB,ym,pB,uu=j(()=>{me();bo();om();Eo();Pv();Dv=60,Bv=8*1024,aB=200,jv=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],iB=jv.filter(e=>e!=="now"&&e!=="track.opened"),gm={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"},cB=3,hm=["commit","push","pr"];wm=e=>sB("sha256").update(String(e)).digest("hex").slice(0,16),Ls=e=>typeof e=="string"?e.trim():"",lB=[[/(^|[^\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"]];ym=e=>String(e?.name||"").startsWith("\u21BB");pB=e=>String(e||"").split(`
|
|
190
|
+
`).slice(0,3).join(`
|
|
191
|
+
`).slice(0,400)});import{randomUUID as wB}from"node:crypto";function _i(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function Yv(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function Jv(e){return`job|${e.box}|id:${e.id}`}async function km(e){return bm.has(e.id)?bm.get(e.id):(du.has(e.id)||du.set(e.id,F(e).then(t=>{let n={keys:new Set,cmdN:0};for(let r of t)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(_i({role:o,sourceId:r.payload.sourceId})),n.keys.add(_i({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(_i({role:"user",sourceId:r.payload.sourceId})),n.keys.add(_i({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(Yv({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(Jv({id:r.payload.id,box:r.boxId}));return bm.set(e.id,n),n}).finally(()=>du.delete(e.id))),du.get(e.id))}async function Xv(e,t){let n=new Set;try{let r=(await km(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=_i(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?iu(o.text):null;if(a){await $(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:a.text,ts:o.ts,keyText:String(o.text||"").slice(0,40),from:await Iv(a,Ii),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=wb(o);if(i){await $(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),Ov(e,{provider:o.agent||null,cls:i.cls,status:i.status,text:o.text}).catch(()=>{});continue}if(o.role!=="user"&&o.box&&n.add(o.box),await $(e,{type:o.role==="user"?"turn.user":"turn.agent",actor:o.role==="user"?"user":"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:o.text,ts:o.ts,model:o.model||null,tools:o.tools||void 0,boxRole:o.boxRole,sourceId:o.sourceId||void 0,note:o.note||void 0,images:o.images?.length?o.images:void 0}}),o.role==="assistant"&&o.box===e.leadBox&&e.workspace?.branch){let c=gx(o.text);if(c&&Gv(await F(e).catch(()=>[])))continue;if(c){let l=kx(c),u;if(e.workspace?.kind==="multi"){let h=await _o(e).catch(()=>[]);u=h.length?h.map(w=>({name:w.name,slug:w.repo??null})):void 0}let f=(e.workspace?.kind==="multi"?null:await Cr(e.cwd).catch(()=>null))||e.workspace.branch,d=await ou(e,{branch:f,rebase:l}).catch(()=>null),p=d?null:bx(c);if(d&&d.available.length===0)continue;await $(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:wB(),ts:o.ts,excerpt:c,branch:f,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:d?.available,defaults:d?.defaults,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||u,offered:d?.defaults||p?.offered||void 0,toPr:p?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function Zv(e,t){try{let n=await km(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=Yv(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await $(e,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),Tv(e,o),Ev(e,o))}}catch{}}async function Qv(e,t){try{let n=await km(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Jv(o);n.keys.has(s)||(n.keys.add(s),await $(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var bm,du,eS=j(()=>{me();bo();om();Av();Eo();um();Nr();dl();pm();uu();bm=new Map,du=new Map});function gB(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
192
|
+
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;t.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return t}async function yB(e,t){let n=`${e.id}:${t.name}`;if(!xm.has(n)){let r=await F(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);xm.set(n,o)}return xm.get(n)}async function tS(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await so(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await yB(e,t),o=gB(await Et(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await $(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:mn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await $(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var xm,nS=j(()=>{me();io();Xt();Dn();xm=new Map});import fu from"node:fs";import Sm from"node:path";import bB from"node:os";async function vB({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(xB,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${t}`},body:JSON.stringify({teamId:0,startDate:String(e),endDate:String(Date.now()),page:o,pageSize:rS})});if(!s.ok)throw new Error(`cursor usage feed: HTTP ${s.status}`);let a=await s.json(),i=a?.usageEventsDisplay||a?.usageEvents||[];if(r.push(...i),i.length<rS)break}return r}function SB(e){if(!e)return null;let t=Number(e.timestamp)||null,n=e.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=e.conversationId||null;return{id:[o||"?",t??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:t,model:e.model||e.modelIntent||null,kind:e.kindLabel||e.kind||null,isHeadless:e.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:e.chargedCents??null}}function sS(){try{return JSON.parse(fu.readFileSync(pu(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function EB(e){try{fu.mkdirSync(Sm.dirname(pu()),{recursive:!0});let t=pu()+".tmp";fu.writeFileSync(t,JSON.stringify(e)),fu.renameSync(t,pu())}catch{}}async function _B({fetchImpl:e,now:t=Date.now()}={}){if(Pr.rows&&t-Pr.at<TB)return Pr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=sS(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await vB({sinceMs:o,cookie:n,fetchImpl:e});Pr={at:t,rows:s.map(SB).filter(Boolean),error:null}}catch(s){Pr={at:t,rows:null,error:String(s?.message||s)}}return Pr.rows}async function IB(e,t){let n=`${e.id}:${t.name}`;if(!vm.has(n)){let r=await F(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);vm.set(n,o)}return vm.get(n)}async function aS(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!oS())return;let r=await _B({fetchImpl:n});if(!r||!r.length)return;let o=await u0(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await IB(e,t),a=sS(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=mn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await $(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}EB({sinceMs:c,seen:[...i].slice(-AB)})}catch{}}var kB,oS,xB,rS,TB,Pr,pu,AB,Tm,vm,Em=j(()=>{me();fp();Dn();kB=process.env.BOSTRAT_STATE_DIR||Sm.join(process.env.HOME||bB.tmpdir(),".bostrat"),oS=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,xB="https://cursor.com/api/dashboard/get-filtered-usage-events",rS=100;TB=5*6e4,Pr={at:0,rows:null,error:null},pu=()=>Sm.join(kB,"cursor-usage.json");AB=5e3;Tm=()=>({enabled:oS(),lastPollAt:Pr.at||null,error:Pr.error}),vm=new Map});import{execFile as $B}from"node:child_process";import{promisify as RB}from"node:util";function Us({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function mu(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function hu({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?PB[r]:null;return a&&s.push(a),s.push(`${uS}: ${Us({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${dS}: ${String(o).trim()}`),s}function iS(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
193
|
+
`)){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===uS.toLowerCase()&&!t.epoch)t.epoch=mu(a);else if(s===dS.toLowerCase()&&!t.model)t.model=a;else if(s===fS.toLowerCase()&&!t.cause){let i=a.toLowerCase();$i.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 DB(e,t=Dr){return e<=t?"rework":"drift"}function BB(e,{window:t=Dr,end:n=$m}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function UB({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return jB.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${fS} trailer`}:FB.test(s)&&!cS.test(s)?{cause:"style",level:"C",basis:"subject"}:cS.test(s)?{cause:"defect",level:"C",basis:"subject"}:LB.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 WB(e){return HB.some(t=>t.test(e))}function qB(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
194
|
+
`)){if(r.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1},t.push(n);continue}if(n){if(r.startsWith("--- ")){let o=r.slice(4).trim();n.oldPath=o==="/dev/null"?null:o.replace(/^a\//,"")}else if(r.startsWith("+++ ")){let o=r.slice(4).trim();n.newPath=o==="/dev/null"?null:o.replace(/^b\//,"")}else if(r.startsWith("Binary files"))n.binary=!0;else if(r.startsWith("@@")){let o=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(r);if(!o)continue;let s=Number(o[1]),a=o[2]==null?1:Number(o[2]),i=o[4]==null?1:Number(o[4]);a>0&&n.deleted.push([s,a]),n.added+=i}}}return t}function KB(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
195
|
+
`)){let o=/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(r);if(o){n={sha:o[1],line:Number(o[3]),time:null},t.set(n.line,n);continue}n&&r.startsWith("committer-time ")&&(n.time=Number(r.slice(15))*1e3)}return t}async function lS(e,t){let{stdout:n}=await Im(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
192
196
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
193
|
-
`)}}async function Dv(e,t,{fixKey:n=null,window:r=$r,resolveOrigin:o=null,context:s={}}={}){let a=await Ov(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=$v(a.message),l=n?ru(n):c.epoch,u=l?Ns(l):null,{stdout:d}=await pm(e,["diff","-U0","--no-color","-M","-C",i,t]),f=QD(d),p=0,m=0,w=0,y=new Map;for(let D of f){if(D.binary)continue;let S=D.oldPath||D.newPath||"",A=D.deleted.reduce((E,[,L])=>E+L,0);if(ZD(S)){w+=A;continue}if(p+=D.added,m+=A,!D.oldPath||!A)continue;let R;try{let E=["blame","-w","-M","-C","--line-porcelain"];for(let[L,U]of D.deleted)E.push("-L",`${L},${L+U-1}`);E.push(i,"--",D.oldPath),R=e1((await pm(e,E)).stdout)}catch{continue}for(let{sha:E,time:L}of R.values()){let U=y.get(E)??{lines:0,time:L};U.lines+=1,y.set(E,U)}}let x=[],T=0,I=new Map;for(let[D,{lines:S,time:A}]of y){let R=I.get(D);if(!R){try{R=await Ov(e,D)}catch{R=null}I.set(D,R)}let E=R?$v(R.message):{epoch:null},L=E.epoch,U=E.provider,se=E.model;if(!L&&o){let te=await o(D,R);te?.trackId&&te?.boxId!=null&&(L={trackId:te.trackId,boxId:te.boxId,epoch:te.epoch??0},U=U??te.provider??null,se=se??te.model??null)}if(!L){T+=S;continue}let G=Number.isFinite(A)?A:R?.ts??a.ts,C=Math.max(0,(a.ts-G)/HD),Z=Ns(L),Y=Z===u?"self":KD(C,r);x.push({sha:D,key:Z,trackId:L.trackId,boxId:L.boxId,epoch:L.epoch,provider:U??null,model:se??null,lines:S,ageDays:Math.round(C*100)/100,class:Y,decay:Y==="rework"?1:Y==="drift"?Math.round(GD(C,{window:r})*1e3)/1e3:0})}x.sort((D,S)=>S.lines-D.lines);let $=YD({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:p,deleted:m,excludedDeleted:w,origins:x,unattributedLines:T,ghost:m===0&&p>0,heuristic:$}}function Bv(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function fm(e){let t=e.genUsd;return{...e,genUsd:Vt(t),reworkChargedUsd:Vt(e.reworkChargedUsd),reworkPendingUsd:Vt(e.reworkPendingUsd),trueUsd:Vt(t+e.reworkChargedUsd),multiplier:t>0?Vt((t+e.reworkChargedUsd)/t):null}}function jv(e,{window:t=$r,boxMeta:n={}}={}){let r=e||[],o=r.filter(S=>S.type==="cost.recorded"),s=r.filter(S=>S.type==="code.committed"),a=r.filter(S=>S.type==="code.rework"),i=new Map;for(let S of r)S.type==="rework.cause.answered"&&S.payload?.sha&&S.payload?.cause&&i.set(S.payload.sha,S.payload.cause);let c=new Set;for(let S of r)S.type==="rework.cause.offered"&&S.payload?.sha&&c.add(S.payload.sha);let l={},u=(S,A,R)=>Ns({trackId:S,boxId:A,epoch:R??0}),d=(S,A={})=>{if(!l[S]){let E=ru(S)||{};l[S]={key:S,...E,provider:null,model:null,...dm()}}let R=l[S];return A.provider&&!R.provider&&(R.provider=A.provider),A.model&&!R.model&&(R.model=A.model),R};for(let S of o){if(!S.boxId)continue;let A=u(S.trackId,S.boxId,S.epoch),{usd:R,model:E}=Ot(S),L=d(A,{provider:S.provider,model:E!=="unknown"?E:null});L.genUsd+=R}let f=new Map;for(let S of s){let A=`${S.trackId}/${S.boxId}`;(f.get(A)??f.set(A,[]).get(A)).push(S)}for(let S of f.values())S.sort((A,R)=>A.ts-R.ts);let p=(S,A,R,E)=>{let L=f.get(`${S}/${A}`)||[],U=-1/0;for(let C of L)C.ts<E&&(U=C.ts);let se=0,G=0;for(let C of o)C.trackId!==S||C.boxId!==A||(C.epoch??0)===(R??0)&&(C.ts<=U||C.ts>E||(se+=Ot(C).usd,G++));return{usd:se,turns:G,from:Number.isFinite(U)?U:null}},m={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0},w={A:0,B:0,C:0,E:0,none:0},y=[],x=[],T={},I={},$=new Set;for(let S of a){let A=S.payload||{};if(!A.sha||$.has(A.sha))continue;$.add(A.sha);let R=ru(A.fixKey)||{trackId:S.trackId,boxId:S.boxId,epoch:S.epoch??0},{usd:E,turns:L}=p(R.trackId,R.boxId,R.epoch,A.ts??S.ts),U=null;i.has(A.sha)?U={cause:i.get(A.sha),level:"B",basis:"operator"}:(A.heuristic?.level&&A.heuristic.level!=="C"||A.heuristic)&&(U=A.heuristic);let se=U?.level??"none";w[se]=(w[se]||0)+1;let G=WD.has(se),C=Number(A.deleted)||0,Z=Array.isArray(A.origins)?A.origins:[],Y=Z.filter(we=>we.class==="rework").reduce((we,ce)=>we+ce.lines,0),te={sha:A.sha,subject:A.subject??"",ts:A.ts??S.ts,trackId:S.trackId,fixKey:A.fixKey??null,fixProvider:A.fixProvider??S.provider??null,fixUsd:Vt(E),fixTurns:L,linesReworked:Y,unattributedLines:Number(A.unattributedLines)||0,ghost:A.ghost===!0,verdict:U?{cause:U.cause,level:se,basis:U.basis??null}:null,charged:Vt(0),origins:[]};if(A.ghost||C===0)m.ghost+=E;else{let we=0;for(let V of Z){let ge=V.lines/C,B=E*ge;if(we+=B,V.class==="self"){m.self+=B;continue}let ye=V.class==="rework"?1:Number(V.decay)||0,be=B*ye;if(m.decayed+=B-be,be<=0)continue;let _e=d(V.key,{provider:V.provider,model:V.model});_e.fixes+=1,_e.linesReworked+=V.class==="rework"?V.lines:0;let Ie=U?.cause&&xi.includes(U.cause)?U.cause:"unclassified";_e.causes[Ie]+=1,G?(_e.reworkChargedUsd+=be,m.charged+=be,te.charged=Vt(te.charged+be)):(_e.reworkPendingUsd+=be,m.pending+=be),te.origins.push({key:V.key,provider:_e.provider,model:_e.model,lines:V.lines,ageDays:V.ageDays,class:V.class,usd:Vt(be)})}let ce=Math.max(0,E-we);m.unattributed+=ce}y.push(te),!i.has(A.sha)&&c.has(A.sha)&&x.push(A.sha)}for(let S of Object.values(l)){let A=S.provider||"unknown",R=S.model||"unknown",E=L=>{L.genUsd+=S.genUsd,L.reworkChargedUsd+=S.reworkChargedUsd,L.reworkPendingUsd+=S.reworkPendingUsd,L.linesReworked+=S.linesReworked,L.fixes+=S.fixes;for(let U of Object.keys(L.causes))L.causes[U]+=S.causes[U]};E(T[A]||=dm()),E(I[R]||=dm())}y.sort((S,A)=>A.ts-S.ts);let D=Object.values(l).map(S=>{let A=n[`${S.trackId}/${S.boxId}`]||{};return fm({...S,trackName:A.trackName??null,label:A.label??null})}).sort((S,A)=>A.reworkChargedUsd+A.reworkPendingUsd-(S.reworkChargedUsd+S.reworkPendingUsd)||A.genUsd-S.genUsd);return{window:t,basis:`rework = lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old; fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause`,byEpoch:Object.fromEntries(D.map(S=>[S.key,S])),epochs:D,byProvider:Object.fromEntries(Object.entries(T).map(([S,A])=>[S,fm(A)])),byModel:Object.fromEntries(Object.entries(I).map(([S,A])=>[S,fm(A)])),buckets:Object.fromEntries(Object.entries(m).map(([S,A])=>[S,Vt(A)])),fixUsdTotal:Vt(Object.values(m).reduce((S,A)=>S+A,0)),fixes:y,verdicts:w,pendingTaps:x,commits:s.length}}var FD,UD,pm,Cv,Mv,Nv,HD,$r,mm,Pv,xi,WD,qD,VD,Rv,zD,JD,XD,Vt,dm,su=j(()=>{gr();FD=LD(jD),UD=64*1024*1024,pm=(e,t)=>FD("git",["-C",e,...t],{maxBuffer:UD,timeout:6e4}),Cv="Bostrat-Epoch",Mv="Bostrat-Model",Nv="Bostrat-Fix-Cause",HD=24*36e5,$r=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,mm=90,Pv=3,xi=Object.freeze(["defect","design","requirements","style"]),WD=new Set(["A","B","E"]);qD={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};VD=/^revert\b|^revert "/i,Rv=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,zD=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,JD=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;XD=[/(^|\/)\.bostrat\//,/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock|Gemfile\.lock|go\.sum|composer\.lock)$/,/\.min\.(js|css)$/,/\.map$/,/\.snap$/,/(^|\/)(node_modules|dist|build|vendor|\.next|target)\//];Vt=e=>Math.round(e*1e4)/1e4,dm=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import Ti from"node:fs/promises";import yt from"node:path";import t1 from"node:os";import n1 from"node:net";import{randomUUID as r1}from"node:crypto";import{execFile as o1}from"node:child_process";import{promisify as s1}from"node:util";function f1(e){for(let t of xm(e))if(!qv.test(t))return t;return null}function Kv(e){let t=xm(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function p1(e,t){let n=Kv(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return yt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return yt.resolve(e,n[r+1])}return e}function h1(e){let t=!1,n=!1;for(let r of xm(e)){if(!t){if(qv.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&hm(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,hm(r))return`serves the filesystem root ("${r}")`;continue}if(hm(r))return`serves the filesystem root ("${r}")`;if(m1(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function g1(e,t){if(yt.resolve(e)===yt.resolve(t))return!0;let[n,r]=await Promise.all([au(e),au(t)]);return n===r}async function y1(e){let n=(await Ti.readdir(e,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await Gv(yt.join(e,o.name,ym))?`./${o.name}`:null))).filter(Boolean)}async function b1(e,t){let[n,r]=await Promise.all([au(e),au(t)]),o=yt.relative(n,r);if(o&&(o.startsWith("..")||yt.isAbsolute(o)))throw vi(`serve directory escapes the Track workspace: ${t}`)}async function k1(e,t){let n=h1(t);if(n)throw vi(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await w1(e))throw vi(`serve directory does not exist: ${e}`);let r=f1(t);if(!r)return;if(!r.includes("/")&&!await Iy(r,{loginShell:!0,failOpen:!0}))throw vi(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(l1.test(t)||u1.has(r)||!c1.has(r)||!Kv(t).includes("run")||d1.test(t))return;let o=p1(e,t);if(await Gv(yt.join(o,ym)))return;let s=await y1(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw vi(`no ${ym} in ${o}${a}`)}async function Wn(){return dn||(wm||=(async()=>{try{dn=JSON.parse(await Ti.readFile(Wv,"utf8"))}catch{dn={views:[],adoptWatermarks:{}}}finally{wm=null}return dn.views||=[],dn.adoptWatermarks||={},dn})(),wm)}async function pn(){await wt(Wv,dn)}function Hn(e,t,n={}){return O({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function Sm(e){return Si.get(e)||null}function zv(e,{name:t,box:n}={}){let r=Si.get(e);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:e,name:t||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Si.set(e,o),o}function bm(e,t){let n=Si.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function iu(e,{error:t=null}={}){let n=Si.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function Jv(e){let t=[];for(let n of e?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||t.push({box:n.box,from:n.startedAt,to:n.endedAt});return t}function km(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function x1(e,t){let n=km(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!Uv.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Uv.has(s))return s}}function Ai(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=n1.connect({port:e,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(t,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function Tm(e){if(process.platform!=="darwin"){await fn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await fn("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function Yv(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function v1(e,t){let n=new Set(e.views.map(o=>o.port)),r=Yv();if(t){let o=Number(t);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=Lv;o<=Fv;o++)if(!(n.has(o)||r.has(o))&&!await Ai(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Lv}-${Fv}`)}async function S1(e){try{let{stdout:t}=await fn(W,q("list-sessions","-F","#{session_name}"));return t.split(`
|
|
194
|
-
`).some(n=>n.trim()===
|
|
197
|
+
`)}}async function mS(e,t,{fixKey:n=null,window:r=Dr,resolveOrigin:o=null,context:s={}}={}){let a=await lS(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=iS(a.message),l=n?mu(n):c.epoch,u=l?Us(l):null,{stdout:f}=await Im(e,["diff","-U0","--no-color","-M","-C",i,t]),d=qB(f),p=0,h=0,w=0,g=new Map;for(let B of d){if(B.binary)continue;let T=B.oldPath||B.newPath||"",_=B.deleted.reduce((I,[,U])=>I+U,0);if(WB(T)){w+=_;continue}if(p+=B.added,h+=_,!B.oldPath||!_)continue;let O;try{let I=["blame","-w","-M","-C","--line-porcelain"];for(let[U,H]of B.deleted)I.push("-L",`${U},${U+H-1}`);I.push(i,"--",B.oldPath),O=KB((await Im(e,I)).stdout)}catch{continue}for(let{sha:I,time:U}of O.values()){let H=g.get(I)??{lines:0,time:U};H.lines+=1,g.set(I,H)}}let k=[],S=0,E=new Map;for(let[B,{lines:T,time:_}]of g){let O=E.get(B);if(!O){try{O=await lS(e,B)}catch{O=null}E.set(B,O)}let I=O?iS(O.message):{epoch:null},U=I.epoch,H=I.provider,ae=I.model;if(!U&&o){let W=await o(B,O);W?.trackId&&W?.boxId!=null&&(U={trackId:W.trackId,boxId:W.boxId,epoch:W.epoch??0},H=H??W.provider??null,ae=ae??W.model??null)}if(!U){S+=T;continue}let X=Number.isFinite(_)?_:O?.ts??a.ts,C=Math.max(0,(a.ts-X)/MB),Q=Us(U),ve=Q===u?"self":DB(C,r);k.push({sha:B,key:Q,trackId:U.trackId,boxId:U.boxId,epoch:U.epoch,provider:H??null,model:ae??null,lines:T,ageDays:Math.round(C*100)/100,class:ve,decay:ve==="rework"?1:ve==="drift"?Math.round(BB(C,{window:r})*1e3)/1e3:0})}k.sort((B,T)=>T.lines-B.lines);let R=UB({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:p,deleted:h,excludedDeleted:w,origins:k,unattributedLines:S,ghost:h===0&&p>0,heuristic:R}}function hS(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function _m(e){let t=e.genUsd;return{...e,genUsd:en(t),reworkChargedUsd:en(e.reworkChargedUsd),reworkPendingUsd:en(e.reworkPendingUsd),trueUsd:en(t+e.reworkChargedUsd),multiplier:t>0?en((t+e.reworkChargedUsd)/t):null}}function wS(e,{window:t=Dr,boxMeta:n={}}={}){let r=e||[],o=r.filter(T=>T.type==="cost.recorded"),s=r.filter(T=>T.type==="code.committed"),a=r.filter(T=>T.type==="code.rework"),i=new Map;for(let T of r)T.type==="rework.cause.answered"&&T.payload?.sha&&T.payload?.cause&&i.set(T.payload.sha,T.payload.cause);let c=new Set;for(let T of r)T.type==="rework.cause.offered"&&T.payload?.sha&&c.add(T.payload.sha);let l={},u=(T,_,O)=>Us({trackId:T,boxId:_,epoch:O??0}),f=(T,_={})=>{if(!l[T]){let I=mu(T)||{};l[T]={key:T,...I,provider:null,model:null,...Am()}}let O=l[T];return _.provider&&!O.provider&&(O.provider=_.provider),_.model&&!O.model&&(O.model=_.model),O};for(let T of o){if(!T.boxId)continue;let _=u(T.trackId,T.boxId,T.epoch),{usd:O,model:I}=Ft(T),U=f(_,{provider:T.provider,model:I!=="unknown"?I:null});U.genUsd+=O}let d=new Map;for(let T of s){let _=`${T.trackId}/${T.boxId}`;(d.get(_)??d.set(_,[]).get(_)).push(T)}for(let T of d.values())T.sort((_,O)=>_.ts-O.ts);let p=(T,_,O,I)=>{let U=d.get(`${T}/${_}`)||[],H=-1/0;for(let C of U)C.ts<I&&(H=C.ts);let ae=0,X=0;for(let C of o)C.trackId!==T||C.boxId!==_||(C.epoch??0)===(O??0)&&(C.ts<=H||C.ts>I||(ae+=Ft(C).usd,X++));return{usd:ae,turns:X,from:Number.isFinite(H)?H:null}},h={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0},w={A:0,B:0,C:0,E:0,none:0},g=[],k=[],S={},E={},R=new Set;for(let T of a){let _=T.payload||{};if(!_.sha||R.has(_.sha))continue;R.add(_.sha);let O=mu(_.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:I,turns:U}=p(O.trackId,O.boxId,O.epoch,_.ts??T.ts),H=null;i.has(_.sha)?H={cause:i.get(_.sha),level:"B",basis:"operator"}:(_.heuristic?.level&&_.heuristic.level!=="C"||_.heuristic)&&(H=_.heuristic);let ae=H?.level??"none";w[ae]=(w[ae]||0)+1;let X=NB.has(ae),C=Number(_.deleted)||0,Q=Array.isArray(_.origins)?_.origins:[],ve=Q.filter(be=>be.class==="rework").reduce((be,se)=>be+se.lines,0),W={sha:_.sha,subject:_.subject??"",ts:_.ts??T.ts,trackId:T.trackId,fixKey:_.fixKey??null,fixProvider:_.fixProvider??T.provider??null,fixUsd:en(I),fixTurns:U,linesReworked:ve,unattributedLines:Number(_.unattributedLines)||0,ghost:_.ghost===!0,verdict:H?{cause:H.cause,level:ae,basis:H.basis??null}:null,charged:en(0),origins:[]};if(_.ghost||C===0)h.ghost+=I;else{let be=0;for(let pe of Q){let ie=pe.lines/C,L=I*ie;if(be+=L,pe.class==="self"){h.self+=L;continue}let ce=pe.class==="rework"?1:Number(pe.decay)||0,ue=L*ce;if(h.decayed+=L-ue,ue<=0)continue;let Ae=f(pe.key,{provider:pe.provider,model:pe.model});Ae.fixes+=1,Ae.linesReworked+=pe.class==="rework"?pe.lines:0;let _e=H?.cause&&$i.includes(H.cause)?H.cause:"unclassified";Ae.causes[_e]+=1,X?(Ae.reworkChargedUsd+=ue,h.charged+=ue,W.charged=en(W.charged+ue)):(Ae.reworkPendingUsd+=ue,h.pending+=ue),W.origins.push({key:pe.key,provider:Ae.provider,model:Ae.model,lines:pe.lines,ageDays:pe.ageDays,class:pe.class,usd:en(ue)})}let se=Math.max(0,I-be);h.unattributed+=se}g.push(W),!i.has(_.sha)&&c.has(_.sha)&&k.push(_.sha)}for(let T of Object.values(l)){let _=T.provider||"unknown",O=T.model||"unknown",I=U=>{U.genUsd+=T.genUsd,U.reworkChargedUsd+=T.reworkChargedUsd,U.reworkPendingUsd+=T.reworkPendingUsd,U.linesReworked+=T.linesReworked,U.fixes+=T.fixes;for(let H of Object.keys(U.causes))U.causes[H]+=T.causes[H]};I(S[_]||=Am()),I(E[O]||=Am())}g.sort((T,_)=>_.ts-T.ts);let B=Object.values(l).map(T=>{let _=n[`${T.trackId}/${T.boxId}`]||{};return _m({...T,trackName:_.trackName??null,label:_.label??null})}).sort((T,_)=>_.reworkChargedUsd+_.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||_.genUsd-T.genUsd);return{window:t,basis:`rework = lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old; fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause`,byEpoch:Object.fromEntries(B.map(T=>[T.key,T])),epochs:B,byProvider:Object.fromEntries(Object.entries(S).map(([T,_])=>[T,_m(_)])),byModel:Object.fromEntries(Object.entries(E).map(([T,_])=>[T,_m(_)])),buckets:Object.fromEntries(Object.entries(h).map(([T,_])=>[T,en(_)])),fixUsdTotal:en(Object.values(h).reduce((T,_)=>T+_,0)),fixes:g,verdicts:w,pendingTaps:k,commits:s.length}}var OB,CB,Im,uS,dS,fS,MB,Dr,$m,pS,$i,NB,PB,jB,cS,LB,FB,HB,en,Am,wu=j(()=>{vr();OB=RB($B),CB=64*1024*1024,Im=(e,t)=>OB("git",["-C",e,...t],{maxBuffer:CB,timeout:6e4}),uS="Bostrat-Epoch",dS="Bostrat-Model",fS="Bostrat-Fix-Cause",MB=24*36e5,Dr=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,$m=90,pS=3,$i=Object.freeze(["defect","design","requirements","style"]),NB=new Set(["A","B","E"]);PB={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};jB=/^revert\b|^revert "/i,cS=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,LB=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,FB=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;HB=[/(^|\/)\.bostrat\//,/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock|Gemfile\.lock|go\.sum|composer\.lock)$/,/\.min\.(js|css)$/,/\.map$/,/\.snap$/,/(^|\/)(node_modules|dist|build|vendor|\.next|target)\//];en=e=>Math.round(e*1e4)/1e4,Am=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import Ci from"node:fs/promises";import At from"node:path";import GB from"node:os";import VB from"node:net";import{randomUUID as zB}from"node:crypto";import{execFile as YB}from"node:child_process";import{promisify as JB}from"node:util";function r1(e){for(let t of Dm(e))if(!vS.test(t))return t;return null}function SS(e){let t=Dm(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function o1(e,t){let n=SS(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return At.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return At.resolve(e,n[r+1])}return e}function a1(e){let t=!1,n=!1;for(let r of Dm(e)){if(!t){if(vS.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Rm(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Rm(r))return`serves the filesystem root ("${r}")`;continue}if(Rm(r))return`serves the filesystem root ("${r}")`;if(s1(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function c1(e,t){if(At.resolve(e)===At.resolve(t))return!0;let[n,r]=await Promise.all([gu(e),gu(t)]);return n===r}async function l1(e){let n=(await Ci.readdir(e,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await TS(At.join(e,o.name,Mm))?`./${o.name}`:null))).filter(Boolean)}async function u1(e,t){let[n,r]=await Promise.all([gu(e),gu(t)]),o=At.relative(n,r);if(o&&(o.startsWith("..")||At.isAbsolute(o)))throw Ri(`serve directory escapes the Track workspace: ${t}`)}async function d1(e,t){let n=a1(t);if(n)throw Ri(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await i1(e))throw Ri(`serve directory does not exist: ${e}`);let r=r1(t);if(!r)return;if(!r.includes("/")&&!await qy(r,{loginShell:!0,failOpen:!0}))throw Ri(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(e1.test(t)||t1.has(r)||!QB.has(r)||!SS(t).includes("run")||n1.test(t))return;let o=o1(e,t);if(await TS(At.join(o,Mm)))return;let s=await l1(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Ri(`no ${Mm} in ${o}${a}`)}async function zn(){return yn||(Om||=(async()=>{try{yn=JSON.parse(await Ci.readFile(xS,"utf8"))}catch{yn={views:[],adoptWatermarks:{}}}finally{Om=null}return yn.views||=[],yn.adoptWatermarks||={},yn})(),Om)}async function kn(){await Tt(xS,yn)}function Vn(e,t,n={}){return $({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function jm(e){return Oi.get(e)||null}function AS(e,{name:t,box:n}={}){let r=Oi.get(e);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:e,name:t||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Oi.set(e,o),o}function Nm(e,t){let n=Oi.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function yu(e,{error:t=null}={}){let n=Oi.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function _S(e){let t=[];for(let n of e?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||t.push({box:n.box,from:n.startedAt,to:n.endedAt});return t}function Pm(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function f1(e,t){let n=Pm(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!bS.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!bS.has(s))return s}}function Mi(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=VB.connect({port:e,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(t,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function Lm(e){if(process.platform!=="darwin"){await bn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await bn("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function IS(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function p1(e,t){let n=new Set(e.views.map(o=>o.port)),r=IS();if(t){let o=Number(t);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=gS;o<=yS;o++)if(!(n.has(o)||r.has(o))&&!await Mi(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${gS}-${yS}`)}async function m1(e){try{let{stdout:t}=await bn(K,V("list-sessions","-F","#{session_name}"));return t.split(`
|
|
198
|
+
`).some(n=>n.trim()===Hs(e))}catch{return!1}}async function Ni(e){try{await bn(K,V("kill-session","-t",Hs(e)))}catch{}}async function Fm(e){if(!await Ha())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Hs(e.slug),n=e.serveCwd??e.cwd,r=At.dirname(process.execPath);await bn(K,V("new-session","-d","-s",t,"-c",n)),await bn(K,V("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await bn(K,V("send-keys","-t",t,"Enter")),await bn(K,V("send-keys","-t",t,"-l",e.cmd)),await bn(K,V("send-keys","-t",t,"Enter"))}async function kS(e,t=12){try{let{stdout:n}=await bn(K,V("capture-pane","-t",Hs(e),"-p"));return String(n||"").trimEnd().split(`
|
|
195
199
|
`).filter(Boolean).slice(-t).join(`
|
|
196
|
-
`)}catch{return""}}async function A1(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await S1(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await Hv(e.slug)||"server exited before listening"),Un.delete(e.id),await Hn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Ai(e.port))return Un.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Hn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>T1?(e.state="error",e.error=await Hv(e.slug)||"never started listening",await Hn(e,"error",{error:e.error}),!0):!1;let o=(Un.get(e.id)||0)+1;return Un.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function cu(){if(!gm){gm=!0;try{let e=await Wn(),t=!1;for(let n of e.views)await A1(n)&&(t=!0);t&&await pn()}finally{gm=!1}}}async function lu({trackId:e}={}){await cu();let t=await Wn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function Xv(e){let n=(await Wn()).views.find(r=>r.id===e);return n?{...n}:null}function Em(e){return dn&&dn.views.find(t=>t.slug===e)||null}async function _m({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=yt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await b1(r,u),await k1(u,n)},f=await Wn();if(i!=="detected"){let w=km(t),y=String(n).trim(),x=f.views.find(T=>T.trackId===e&&(T.slug===w||km(T.name)===w||T.cmd===y&&yt.resolve(T.serveCwd??T.cwd)===u));if(x)return E1(f,x,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(w=>w.state==="live"||w.state==="starting").length>=Rr)throw new Error(`view cap reached (${Rr} live) \u2014 stop one first`);let m={id:r1().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:x1(f,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await v1(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Am(m),f.views.push(m),await pn(),await Hn(m,"created",{origin:i}),bm(e,m.id),i==="detected"&&await Ai(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await pn(),await Hn(m,"live")),{...m}}async function E1(e,t,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(t.box=r),o&&(t.branch=o);let i=t.cmd!==n,c=s!==void 0&&!await g1(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Ai(t.port))return await pn(),bm(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(d=>d.id!==t.id&&(d.state==="live"||d.state==="starting")).length>=Rr)throw new Error(`view cap reached (${Rr} live) \u2014 stop one first`);return t.origin==="detected"?(await Tm(t.port),t.origin="agent"):await Ei(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Un.delete(t.id),await Am(t),await pn(),await Hn(t,"created",{restarted:!0,reused:!0}),bm(t.trackId,t.id),{...t,reused:!0}}async function Zv(e){let n=(await Wn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Tm(n.port):await Ei(n.slug),n.state!=="stopped"&&(n.state="stopped",Un.delete(n.id),await pn(),await Hn(n,"stopped")),{...n}}async function Qv(e){let t=await Wn(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=Rr)throw new Error(`view cap reached (${Rr} live) \u2014 stop one first`);return await Ei(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Un.delete(n.id),await Am(n),await pn(),await Hn(n,"created",{restarted:!0}),{...n}}async function eS(e){let t=await Wn(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await Tm(r.port):await Ei(r.slug),t.views.splice(n,1),Un.delete(r.id),await pn(),await Hn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function tS(e){let t=await Wn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Ei(r.slug),Un.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await pn()),n.length}async function nS(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Wn(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await pn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(_1)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(!Yv().has(c)&&!n.views.some(l=>l.port===c)&&await Ai(c))try{i.push(await _m({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var fn,a1,Wv,Lv,Fv,Rr,i1,Ps,Uv,vi,c1,ym,l1,u1,d1,xm,qv,hm,m1,w1,Gv,au,dn,wm,Si,vm,Vv,Un,T1,gm,_1,uu=j(()=>{nt();ue();rr();fn=s1(o1),a1=process.env.BOSTRAT_STATE_DIR||yt.join(process.env.HOME||t1.tmpdir(),".bostrat"),Wv=yt.join(a1,"views.json"),[Lv,Fv]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Rr=Number(process.env.BOSTRAT_VIEW_MAX)||3,i1="bostrat-view-",Ps=e=>`${i1}${e}`,Uv=new Set(["bostrat","www","api","views","app"]),vi=e=>Object.assign(new Error(e),{status:400}),c1=new Set(["npm","pnpm","yarn","bun"]),ym="package.json",l1=/[|;&`]|\$\(/,u1=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),d1=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,xm=e=>String(e).trim().split(/\s+/).filter(Boolean),qv=/^[A-Za-z_][A-Za-z0-9_]*=/;hm=e=>e==="/",m1=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");w1=e=>Ti.stat(e).then(t=>t.isDirectory(),()=>!1),Gv=e=>Ti.stat(e).then(t=>t.isFile(),()=>!1),au=e=>Ti.realpath(e).catch(()=>yt.resolve(e));dn=null,wm=null;Si=new Map,vm=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Vv=12e3;Un=new Map,T1=12e4;gm=!1;_1=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function du(e,t=Date.now()){let n=null;for(let r of e||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&t-n.ts>$1?null:n}function R1(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function rS(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await F(e)}catch{return}let r=du(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await wo(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Im.get(e.id)||0)<I1)return;Im.set(e.id,s);let a;try{a=await cn(e.cwd,{caller:"reconcile"})}catch{return}let i=await Os(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await xe(e.cwd).catch(()=>null);if(l){let{link:u,created:d}=await St({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!d&&!(u?.trackId===e.id&&await Nn(l,c.number))){if(!R1(u,r))return;let f=await F(e).catch(()=>null);if(f&&du(f)==null)return;await O(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await O(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),Im.delete(e.id)}var I1,Im,$1,$m=j(()=>{ue();$s();tr();Pn();gi();Cs();I1=6e3,Im=new Map,$1=30*6e4});import Rm from"node:os";import{execFile as O1}from"node:child_process";import{promisify as C1}from"node:util";async function fu(){if(process.platform!=="darwin")return Rm.freemem();try{let{stdout:e}=await M1("vm_stat",[],{timeout:3e3}),t=Number(/page size of (\d+) bytes/.exec(e)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(e)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*t;return r>0?r:Rm.freemem()}catch{return Rm.freemem()}}var M1,Om=j(()=>{M1=C1(O1)});import We from"node:fs/promises";import Ke from"node:path";import iS from"node:os";import{randomUUID as N1}from"node:crypto";import{execFile as P1}from"node:child_process";import{promisify as D1}from"node:util";import{chromium as B1}from"playwright-core";async function wn(){return go||(Cm||=(async()=>{try{go=JSON.parse(await We.readFile(lS,"utf8"))}catch{go={mocks:[]}}finally{Cm=null}return go.mocks||=[],go})(),Cm)}async function Mt(){await wt(lS,go)}function pS(e,t){hn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&hn(e,t.v,"png")}function ut(e,t,n={}){return O({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function Nt(e,t){let n=Ii(e.cwd);return{html:Ke.join(n,`${e.id}.v${t}.html`),png:Ke.join(n,`${e.id}.v${t}.png`)}}function _i(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Ke.join(e.cwd,r):null}function zt(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function Pm(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function pu(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(q1)}function K1(e){return!Buffer.isBuffer(e)||e.length<32?null:e.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:e.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function G1(e){let t=Buffer.alloc(24),n=await We.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function V1({imgW:e,imgH:t,bbox:n,pad:r,align:o=1}){if(!n||!(e>0)||!(t>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=e*.9&&a>=t*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function z1(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>f&&(f=y),m<d&&(d=m),p=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},t.toString("base64"))}async function J1(e,t,{bg:n,crop:r}){let o=await e.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function Y1({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await B1.launch({executablePath:uS,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Tt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ds})).newPage();a.setDefaultTimeout(Bs),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Bs}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Bs});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await z1(a,i),u=l&&V1({...l,pad:F1*Ds,align:Ds});u&&(i=await J1(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<dS)throw new Tt(`capture produced a ${i.length}-byte image`);let c=Pm(i);if(!c)throw new Tt("output is not a PNG");return await We.writeFile(t,i),{width:Math.round(c.width/Ds),height:Math.round(c.height/Ds),dpr:Ds,renderMs:Date.now()-r}}catch(s){throw s instanceof Tt?s:new Tt(/timeout/i.test(s?.message||"")?`render timed out after ${Bs}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function aS({htmlPath:e,outPath:t,width:n}){let r=await We.mkdtemp(Ke.join(iS.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Nm(oS,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Bs,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Tt(`firefox not found (${oS})`,""):new Tt(c?.killed?`render timed out after ${Bs}ms`:"firefox exited non-zero",s)}let a=await We.stat(t).catch(()=>null);if(!a||a.size<dS)throw new Tt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Pm(await G1(t));if(!i)throw new Tt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await We.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function X1(e){if(j1==="firefox")return aS(e);try{return await Y1(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await aS(e)}catch(n){throw/not found/.test(n?.message||"")?new Tt(`no browser installed \u2014 chromium missing at ${uS} and no firefox on PATH; install chromium (Debian: apt install chromium fonts-dejavu-core fonts-liberation) or point BOSTRAT_CHROMIUM_BIN at a Chrome/Chromium binary`,t?.message||""):n}}}function mS(e){return Mm=Mm.then(e,e),Mm}async function hS(e,t){let n=await wn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Mt(),await ut(r,"rendering",{version:t});try{let o=await fu();if(o<U1)throw new Tt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Nt(r,t),a=await Z1({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await We.rm(s.png,{force:!0}).catch(()=>{});return}Dm(r,{v:t,source:"html",htmlRel:Ke.relative(r.cwd,s.html),pngRel:Ke.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Mt(),await ut(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),mn(r),hn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Mt(),await ut(r,"error",{version:t,error:r.error}),mn(r)}}}function Dm(e,t){e.versions=(e.versions||[]).filter(r=>r.v!==t.v).concat(t),e.versions.sort((r,o)=>r.v-o.v);let n=e.versions[e.versions.length-1];e.readyVersion=n.v,e.height=n.height,e.dpr=n.dpr,e.renderMs=n.renderMs??null,n.v===e.version&&(e.state="ready",e.error=null)}function Q1({trackId:e,name:t,cwd:n,width:r}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?L1:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function $i({trackId:e,group:t}={}){let n=await wn(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function qn(e){let n=(await wn()).mocks.find(r=>r.id===e);return n?{...n}:null}function wS({html:e,image:t,video:n}){if([typeof e=="string"&&e.trim(),!!t,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(t&&!pu(t))throw new Error("image must be a PNG");if(n){let o=K1(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>sS)throw new Error(`video must be ${sS/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function gS({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=Q1({trackId:e,name:t,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=wS({html:n,image:r,video:o}),m=await wn(),w={id:N1().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:f?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await We.mkdir(Ii(s),{recursive:!0}),m.mocks.push(w),f?(await We.writeFile(Nt(w,1).html,n),await Mt(),await ut(w,"created"),mn(w),hn(w,1,"html"),mS(()=>hS(w.id,1))):o?(await bS(w,1,o,p),await Mt(),await ut(w,"created"),await ut(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),mn(w),pS(w,w.versions[0])):(await kS(w,1,r),await Mt(),await ut(w,"created"),await ut(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),mn(w),hn(w,1,"png")),{...w}}async function yS(e,{html:t,image:n,video:r,width:o}={}){let a=(await wn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=wS({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await We.mkdir(Ii(a.cwd),{recursive:!0}),i?(a.state="queued",await We.writeFile(Nt(a,l).html,t),await Mt(),await ut(a,"updated",{version:l}),mn(a),hn(a,l,"html"),mS(()=>hS(a.id,l))):r?(await bS(a,l,r,c),await Mt(),await ut(a,"updated",{version:l}),await ut(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:zt(a)?.durationS??null}),mn(a),pS(a,a.versions.find(u=>u.v===l))):(await kS(a,l,n),await Mt(),await ut(a,"updated",{version:l}),await ut(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),mn(a),hn(a,l,"png")),{...a}}async function eB(e){try{let{stdout:t}=await Nm(H1,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:fS,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(t),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function tB(e,t){try{await Nm(W1,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:fS,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await We.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function bS(e,t,n,r){let o=Ii(e.cwd),s=Ke.join(o,`${e.id}.v${t}.${r.ext}`),a=Ke.join(o,`${e.id}.v${t}.poster.png`);await We.writeFile(s,n);let i=await eB(s),c=await tB(s,a);Dm(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Ke.relative(e.cwd,s),posterRel:c?Ke.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function kS(e,t,n){let r=Pm(n);if(!r)throw new Error("image must be a PNG");let o=Nt(e,t);await We.writeFile(o.png,n),Dm(e,{v:t,source:"image",htmlRel:null,pngRel:Ke.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function xS(e){let t=await wn(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await ut(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function vS(e){let t=await wn(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Nt(r,o);await We.rm(s.html,{force:!0}).catch(()=>{}),await We.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await We.rm(Ke.join(r.cwd,s),{force:!0}).catch(()=>{});return await Mt(),await ut(r,"deleted"),Ft("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function SS(e){let n=(await wn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Ke.join(cS,"mock-archive",e.id),o=Ii(e.cwd),s=Ke.join(r,".bostrat","mocks");await We.mkdir(s,{recursive:!0});let a=await We.readdir(o).catch(()=>[]);for(let i of a)await We.copyFile(Ke.join(o,i),Ke.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await We.stat(Ke.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),mn(i)}return await Mt(),n.length}async function TS(e){let t=await wn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Mt();for(let r of n)Ft("mocks",r.id).catch(()=>{})}return n.length}async function AS(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await wn();for(let r of n.mocks){await mn(r);for(let o of r.versions||[])o.htmlRel&&await hn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await hn(r,o.v,"png"),o.videoRel&&await hn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Nm,cS,lS,j1,uS,oS,L1,Ds,F1,dS,Bs,U1,sS,H1,W1,fS,Tt,go,Cm,mn,hn,Ii,q1,Z1,Mm,js=j(()=>{ue();rr();Om();sr();Nm=D1(P1),cS=process.env.BOSTRAT_STATE_DIR||Ke.join(process.env.HOME||iS.tmpdir(),".bostrat"),lS=Ke.join(cS,"mocks.json"),j1=process.env.BOSTRAT_MOCK_ENGINE||"chromium",uS=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",oS=process.env.BOSTRAT_FIREFOX_BIN||"firefox",L1=1300,Ds=2,F1=32,dS=1500,Bs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,U1=700*1024*1024,sS=100*1024*1024,H1=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",W1=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",fS=2e4,Tt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
200
|
+
`)}catch{return""}}async function w1(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await m1(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await kS(e.slug)||"server exited before listening"),Gn.delete(e.id),await Vn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Mi(e.port))return Gn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Vn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>h1?(e.state="error",e.error=await kS(e.slug)||"never started listening",await Vn(e,"error",{error:e.error}),!0):!1;let o=(Gn.get(e.id)||0)+1;return Gn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function bu(){if(!Cm){Cm=!0;try{let e=await zn(),t=!1;for(let n of e.views)await w1(n)&&(t=!0);t&&await kn()}finally{Cm=!1}}}async function ku({trackId:e}={}){await bu();let t=await zn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function $S(e){let n=(await zn()).views.find(r=>r.id===e);return n?{...n}:null}function Um(e){return yn&&yn.views.find(t=>t.slug===e)||null}async function Hm({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=At.resolve(r,o??"."),f=i==="detected"?null:async()=>{await u1(r,u),await d1(u,n)},d=await zn();if(i!=="detected"){let w=Pm(t),g=String(n).trim(),k=d.views.find(S=>S.trackId===e&&(S.slug===w||Pm(S.name)===w||S.cmd===g&&At.resolve(S.serveCwd??S.cwd)===u));if(k)return g1(d,k,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);let h={id:zB().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:f1(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await p1(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Fm(h),d.views.push(h),await kn(),await Vn(h,"created",{origin:i}),Nm(e,h.id),i==="detected"&&await Mi(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await kn(),await Vn(h,"live")),{...h}}async function g1(e,t,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(t.box=r),o&&(t.branch=o);let i=t.cmd!==n,c=s!==void 0&&!await c1(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Mi(t.port))return await kn(),Nm(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);return t.origin==="detected"?(await Lm(t.port),t.origin="agent"):await Ni(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Gn.delete(t.id),await Fm(t),await kn(),await Vn(t,"created",{restarted:!0,reused:!0}),Nm(t.trackId,t.id),{...t,reused:!0}}async function RS(e){let n=(await zn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Lm(n.port):await Ni(n.slug),n.state!=="stopped"&&(n.state="stopped",Gn.delete(n.id),await kn(),await Vn(n,"stopped")),{...n}}async function OS(e){let t=await zn(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=Br)throw new Error(`view cap reached (${Br} live) \u2014 stop one first`);return await Ni(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Gn.delete(n.id),await Fm(n),await kn(),await Vn(n,"created",{restarted:!0}),{...n}}async function CS(e){let t=await zn(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await Lm(r.port):await Ni(r.slug),t.views.splice(n,1),Gn.delete(r.id),await kn(),await Vn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function MS(e){let t=await zn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Ni(r.slug),Gn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await kn()),n.length}async function NS(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await zn(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await kn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(y1)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(!IS().has(c)&&!n.views.some(l=>l.port===c)&&await Mi(c))try{i.push(await Hm({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var bn,XB,xS,gS,yS,Br,ZB,Hs,bS,Ri,QB,Mm,e1,t1,n1,Dm,vS,Rm,s1,i1,TS,gu,yn,Om,Oi,Bm,ES,Gn,h1,Cm,y1,xu=j(()=>{lt();me();cr();bn=JB(YB),XB=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||GB.tmpdir(),".bostrat"),xS=At.join(XB,"views.json"),[gS,yS]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Br=Number(process.env.BOSTRAT_VIEW_MAX)||3,ZB="bostrat-view-",Hs=e=>`${ZB}${e}`,bS=new Set(["bostrat","www","api","views","app"]),Ri=e=>Object.assign(new Error(e),{status:400}),QB=new Set(["npm","pnpm","yarn","bun"]),Mm="package.json",e1=/[|;&`]|\$\(/,t1=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),n1=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Dm=e=>String(e).trim().split(/\s+/).filter(Boolean),vS=/^[A-Za-z_][A-Za-z0-9_]*=/;Rm=e=>e==="/",s1=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");i1=e=>Ci.stat(e).then(t=>t.isDirectory(),()=>!1),TS=e=>Ci.stat(e).then(t=>t.isFile(),()=>!1),gu=e=>Ci.realpath(e).catch(()=>At.resolve(e));yn=null,Om=null;Oi=new Map,Bm=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,ES=12e3;Gn=new Map,h1=12e4;Cm=!1;y1=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function vu(e,t=Date.now()){let n=null;for(let r of e||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&t-n.ts>k1?null:n}function x1(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function PS(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await F(e)}catch{return}let r=vu(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await To(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Wm.get(e.id)||0)<b1)return;Wm.set(e.id,s);let a;try{a=await hn(e.cwd,{caller:"reconcile"})}catch{return}let i=await js(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await Se(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await Ot({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await Ln(l,c.number))){if(!x1(u,r))return;let d=await F(e).catch(()=>null);if(d&&vu(d)==null)return;await $(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await $(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),Wm.delete(e.id)}var b1,Wm,k1,qm=j(()=>{me();Bs();ar();Fn();Si();Eo();b1=6e3,Wm=new Map,k1=30*6e4});import Km from"node:os";import{execFile as v1}from"node:child_process";import{promisify as S1}from"node:util";async function Su(){if(process.platform!=="darwin")return Km.freemem();try{let{stdout:e}=await T1("vm_stat",[],{timeout:3e3}),t=Number(/page size of (\d+) bytes/.exec(e)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(e)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*t;return r>0?r:Km.freemem()}catch{return Km.freemem()}}var T1,Gm=j(()=>{T1=S1(v1)});import Ke from"node:fs/promises";import ze from"node:path";import LS from"node:os";import{randomUUID as E1}from"node:crypto";import{execFile as A1}from"node:child_process";import{promisify as _1}from"node:util";import{chromium as I1}from"playwright-core";async function Sn(){return Io||(Vm||=(async()=>{try{Io=JSON.parse(await Ke.readFile(US,"utf8"))}catch{Io={mocks:[]}}finally{Vm=null}return Io.mocks||=[],Io})(),Vm)}async function Ht(){await Tt(US,Io)}function KS(e,t){vn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&vn(e,t.v,"png")}function yt(e,t,n={}){return $({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function Wt(e,t){let n=Di(e.cwd);return{html:ze.join(n,`${e.id}.v${t}.html`),png:ze.join(n,`${e.id}.v${t}.png`)}}function Pi(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?ze.join(e.cwd,r):null}function tn(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function Jm(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Tu(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(P1)}function D1(e){return!Buffer.isBuffer(e)||e.length<32?null:e.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:e.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function B1(e){let t=Buffer.alloc(24),n=await Ke.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function j1({imgW:e,imgH:t,bbox:n,pad:r,align:o=1}){if(!n||!(e>0)||!(t>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=e*.9&&a>=t*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function L1(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let h=0;h<s;h++)for(let w=h*o*4,g=0;g<o;g++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(g<u&&(u=g),g>d&&(d=g),h<f&&(f=h),p=h);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},t.toString("base64"))}async function F1(e,t,{bg:n,crop:r}){let o=await e.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((f,d)=>{c.onload=f,c.onerror=()=>d(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function U1({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await I1.launch({executablePath:HS,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ct("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ws})).newPage();a.setDefaultTimeout(qs),await a.goto(`file://${e}`,{waitUntil:"load",timeout:qs}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:qs});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await L1(a,i),u=l&&j1({...l,pad:O1*Ws,align:Ws});u&&(i=await F1(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<WS)throw new Ct(`capture produced a ${i.length}-byte image`);let c=Jm(i);if(!c)throw new Ct("output is not a PNG");return await Ke.writeFile(t,i),{width:Math.round(c.width/Ws),height:Math.round(c.height/Ws),dpr:Ws,renderMs:Date.now()-r}}catch(s){throw s instanceof Ct?s:new Ct(/timeout/i.test(s?.message||"")?`render timed out after ${qs}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function jS({htmlPath:e,outPath:t,width:n}){let r=await Ke.mkdtemp(ze.join(LS.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Ym(DS,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:qs,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Ct(`firefox not found (${DS})`,""):new Ct(c?.killed?`render timed out after ${qs}ms`:"firefox exited non-zero",s)}let a=await Ke.stat(t).catch(()=>null);if(!a||a.size<WS)throw new Ct(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Jm(await B1(t));if(!i)throw new Ct("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Ke.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function H1(e){if($1==="firefox")return jS(e);try{return await U1(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await jS(e)}catch(n){throw/not found/.test(n?.message||"")?new Ct(`no browser installed \u2014 chromium missing at ${HS} and no firefox on PATH; install chromium (Debian: apt install chromium fonts-dejavu-core fonts-liberation) or point BOSTRAT_CHROMIUM_BIN at a Chrome/Chromium binary`,t?.message||""):n}}}function GS(e){return zm=zm.then(e,e),zm}async function VS(e,t){let n=await Sn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Ht(),await yt(r,"rendering",{version:t});try{let o=await Su();if(o<C1)throw new Ct(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Wt(r,t),a=await W1({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Ke.rm(s.png,{force:!0}).catch(()=>{});return}Xm(r,{v:t,source:"html",htmlRel:ze.relative(r.cwd,s.html),pngRel:ze.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Ht(),await yt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),xn(r),vn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Ht(),await yt(r,"error",{version:t,error:r.error}),xn(r)}}}function Xm(e,t){e.versions=(e.versions||[]).filter(r=>r.v!==t.v).concat(t),e.versions.sort((r,o)=>r.v-o.v);let n=e.versions[e.versions.length-1];e.readyVersion=n.v,e.height=n.height,e.dpr=n.dpr,e.renderMs=n.renderMs??null,n.v===e.version&&(e.state="ready",e.error=null)}function q1({trackId:e,name:t,cwd:n,width:r}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?R1:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Bi({trackId:e,group:t}={}){let n=await Sn(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function Yn(e){let n=(await Sn()).mocks.find(r=>r.id===e);return n?{...n}:null}function zS({html:e,image:t,video:n}){if([typeof e=="string"&&e.trim(),!!t,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(t&&!Tu(t))throw new Error("image must be a PNG");if(n){let o=D1(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>BS)throw new Error(`video must be ${BS/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function YS({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=q1({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=zS({html:n,image:r,video:o}),h=await Sn(),w={id:E1().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Ke.mkdir(Di(s),{recursive:!0}),h.mocks.push(w),d?(await Ke.writeFile(Wt(w,1).html,n),await Ht(),await yt(w,"created"),xn(w),vn(w,1,"html"),GS(()=>VS(w.id,1))):o?(await XS(w,1,o,p),await Ht(),await yt(w,"created"),await yt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),xn(w),KS(w,w.versions[0])):(await ZS(w,1,r),await Ht(),await yt(w,"created"),await yt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),xn(w),vn(w,1,"png")),{...w}}async function JS(e,{html:t,image:n,video:r,width:o}={}){let a=(await Sn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=zS({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await Ke.mkdir(Di(a.cwd),{recursive:!0}),i?(a.state="queued",await Ke.writeFile(Wt(a,l).html,t),await Ht(),await yt(a,"updated",{version:l}),xn(a),vn(a,l,"html"),GS(()=>VS(a.id,l))):r?(await XS(a,l,r,c),await Ht(),await yt(a,"updated",{version:l}),await yt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:tn(a)?.durationS??null}),xn(a),KS(a,a.versions.find(u=>u.v===l))):(await ZS(a,l,n),await Ht(),await yt(a,"updated",{version:l}),await yt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),xn(a),vn(a,l,"png")),{...a}}async function K1(e){try{let{stdout:t}=await Ym(M1,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:qS,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(t),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function G1(e,t){try{await Ym(N1,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:qS,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Ke.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function XS(e,t,n,r){let o=Di(e.cwd),s=ze.join(o,`${e.id}.v${t}.${r.ext}`),a=ze.join(o,`${e.id}.v${t}.poster.png`);await Ke.writeFile(s,n);let i=await K1(s),c=await G1(s,a);Xm(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:ze.relative(e.cwd,s),posterRel:c?ze.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function ZS(e,t,n){let r=Jm(n);if(!r)throw new Error("image must be a PNG");let o=Wt(e,t);await Ke.writeFile(o.png,n),Xm(e,{v:t,source:"image",htmlRel:null,pngRel:ze.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function QS(e){let t=await Sn(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await yt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function eT(e){let t=await Sn(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Wt(r,o);await Ke.rm(s.html,{force:!0}).catch(()=>{}),await Ke.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Ke.rm(ze.join(r.cwd,s),{force:!0}).catch(()=>{});return await Ht(),await yt(r,"deleted"),Vt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function tT(e){let n=(await Sn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=ze.join(FS,"mock-archive",e.id),o=Di(e.cwd),s=ze.join(r,".bostrat","mocks");await Ke.mkdir(s,{recursive:!0});let a=await Ke.readdir(o).catch(()=>[]);for(let i of a)await Ke.copyFile(ze.join(o,i),ze.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Ke.stat(ze.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),xn(i)}return await Ht(),n.length}async function nT(e){let t=await Sn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Ht();for(let r of n)Vt("mocks",r.id).catch(()=>{})}return n.length}async function rT(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Sn();for(let r of n.mocks){await xn(r);for(let o of r.versions||[])o.htmlRel&&await vn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await vn(r,o.v,"png"),o.videoRel&&await vn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Ym,FS,US,$1,HS,DS,R1,Ws,O1,WS,qs,C1,BS,M1,N1,qS,Ct,Io,Vm,xn,vn,Di,P1,W1,zm,Ks=j(()=>{me();cr();Gm();ur();Ym=_1(A1),FS=process.env.BOSTRAT_STATE_DIR||ze.join(process.env.HOME||LS.tmpdir(),".bostrat"),US=ze.join(FS,"mocks.json"),$1=process.env.BOSTRAT_MOCK_ENGINE||"chromium",HS=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",DS=process.env.BOSTRAT_FIREFOX_BIN||"firefox",R1=1300,Ws=2,O1=32,WS=1500,qs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,C1=700*1024*1024,BS=100*1024*1024,M1=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",N1=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",qS=2e4,Ct=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
197
201
|
${n.trim().split(`
|
|
198
202
|
`).slice(-6).join(`
|
|
199
|
-
`)}`:t),this.name="MockRenderError"}},go=null,Cm=null;mn=e=>ct("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),hn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?_i(e,r,"video"):r&&_i(e,r,n)||(n==="html"?Nt(e,t).html:null);return o?or("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ii=e=>Ke.join(e,".bostrat","mocks");q1=Buffer.from([137,80,78,71,13,10,26,10]);Z1=X1,Mm=Promise.resolve()});import nB from"node:fs/promises";import RS from"node:path";import rB from"node:os";import{randomUUID as OS}from"node:crypto";async function Gn(){return Kn||(Bm||=(async()=>{let e=!0;try{Kn=JSON.parse(await nB.readFile(CS,"utf8"))}catch{Kn={flows:[]},e=!1}finally{Bm=null}if(Kn.flows||=[],!e&&Me()){let{records:t,source:n}=await Ye("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Kn.flows.push(r.data);Kn.flows.length&&await Ls().catch(()=>{})}}return Kn})(),Bm)}async function Ls(){await wt(CS,Kn)}function jm(e,t,n={}){return O({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function MS({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>ES)throw new Error(`steps must be an array of 1-${ES}`);let r=await $i({trackId:e}),o=[...n],s=[];for(let i=0;i<t.length;i++){let c=t[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>IS)throw new Error(`${l}: caption over ${IS} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>$S)throw new Error(`${l}: title over ${$S} chars`);let f=String(c.mock??"").trim().replace(/^mock:/,"");if(!f)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(T=>T.id===f);if(p.length||(p=r.filter(T=>T.name.toLowerCase()===f.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${f}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${f}" is ambiguous (${p.map(T=>`mock:${T.id}`).join(", ")}) \u2014 use the id`);let m=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(T=>T.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(T=>T.mockId===m.id),x=y>=0?o.splice(y,1)[0].id:OS().slice(0,8);s.push({id:x,mockId:m.id,caption:u,title:d,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function sB(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>_S)throw new Error(`name over ${_S} chars`);return e.trim()}async function NS({trackId:e}={}){let t=await Gn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function aB(e){let n=(await Gn()).flows.find(r=>r.id===e);return n?{...n}:null}async function PS(e){let t=await aB(e);if(!t)return null;let n=new Map((await $i({trackId:t.trackId})).map(r=>[r.id,r]));return t.steps=t.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=zt(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),t}async function DS({trackId:e,name:t,steps:n,cwd:r,box:o,agent:s}){if(!e)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=sB(t),i=await Gn(),c=i.flows.find(p=>p.trackId===e&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await MS({trackId:e,steps:n}),d=Date.now(),f={id:OS().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:d}],steps:l,createdAt:d,updatedAt:d};return i.flows.push(f),await Ls(),await jm(f,"created"),mu(f),{...f}}async function BS(e,{steps:t,note:n}={}){let o=(await Gn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await MS({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await Ls(),await jm(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),mu(o),{...o}}async function jS(e){let t=await Gn(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Ls(),await jm(r,"deleted"),Ft("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function LS(e){return(await Gn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function FS(e){let n=(await Gn()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),mu(r);return n.length&&await Ls(),n.length}async function US(e){let t=await Gn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Ls();for(let r of n)Ft("flows",r.id).catch(()=>{})}return n.length}async function HS(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Gn();for(let r of n.flows)await mu(r);return{pushed:n.flows.length,source:"plane"}}var oB,CS,ES,_S,IS,$S,Kn,Bm,mu,Lm=j(()=>{ue();rr();js();sr();oB=process.env.BOSTRAT_STATE_DIR||RS.join(process.env.HOME||rB.tmpdir(),".bostrat"),CS=RS.join(oB,"flows.json"),ES=20,_S=80,IS=500,$S=80,Kn=null,Bm=null;mu=e=>ct("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Jn from"node:fs/promises";import Yn from"node:path";import iB from"node:os";import{randomUUID as cB}from"node:crypto";function Ri(e){return lB.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function gn(){return Vn||(Fm||=(async()=>{let e=!0;try{Vn=JSON.parse(await Jn.readFile(qS,"utf8"))}catch{Vn={runbooks:[]},e=!1}finally{Fm=null}return Vn.runbooks||=[],!e&&Me()&&await uB().catch(()=>{}),Vn})(),Fm)}async function bo(){await wt(qS,Vn)}function wu(e){return Oi(ct("runbooks",{...e},{trackId:e.trackId}))}async function uB(){let{records:e,source:t}=await Ye("runbooks");if(!(t!=="plane"||!e.length)){await Jn.mkdir(hu,{recursive:!0});for(let n of e){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await $a("runbooks",r.id,o.v,"md");typeof s=="string"&&await Jn.writeFile(zn(r,o.v),s).catch(()=>{})}Vn.runbooks.push(r)}Vn.runbooks.length&&await bo()}}function Fs(e,t,n={}){return O({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function zn(e,t){return Yn.join(hu,`${e.id}.v${t}.md`)}function KS(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function GS(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>WS)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${WS/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ri(e);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return t}function dB(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function fB({trackId:e,status:t}={}){let n=await gn(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function VS({trackId:e,status:t}={}){let n=await gn(),r=await fB({trackId:e,status:t});if(!Me())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Ye("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Um(e){let t=await pB(e);if(t)return t;if(!Me())return null;let{records:n,source:r}=await Ye("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function zS(e,t){let n=await $a("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function pB(e){let n=(await gn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function JS({trackId:e,trackName:t,name:n,md:r,cwd:o,box:s,agent:a}){if(!e)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=GS(r),c=await gn(),l={id:cB().slice(0,8),trackId:e,trackName:t||e,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await Jn.mkdir(hu,{recursive:!0}),await Jn.writeFile(zn(l,1),r),l.versions.push({v:1,mdRel:Yn.relative(yo,zn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await bo(),await Fs(l,"created",{version:1}),wu(l),Oi(or("runbooks",l.id,1,"md",zn(l,1))),{...l}}async function YS(e,{md:t,note:n}={}){let o=(await gn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");dB(o,"update");let s=GS(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Jn.mkdir(hu,{recursive:!0}),await Jn.writeFile(zn(o,a),t),o.versions.push({v:a,mdRel:Yn.relative(yo,zn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await bo(),await Fs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),wu(o),Oi(or("runbooks",o.id,a,"md",zn(o,a))),{...o}}async function XS(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await gn()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await bo(),await Fs(r,t==="done"?"done":"reopened"),wu(r),{...r})}async function ZS(e){let t=await gn(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await Jn.rm(Yn.join(yo,o.mdRel),{force:!0}).catch(()=>{});return await bo(),r.trackDeletedAt||await Fs(r,"deleted"),Oi(Ft("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function QS(e){let n=(await gn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Fs(r,"orphaned"),r.cwd=null,wu(r);return n.length&&await bo(),n.length}async function eT(e){let t=await gn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Fs(r,"deleted");for(let o of r.versions||[])await Jn.rm(Yn.join(yo,o.mdRel),{force:!0}).catch(()=>{});Oi(Ft("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await bo()),n.length}async function tT(){if(!Me())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Ye("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await gn();for(let r of n.runbooks){await ct("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await or("runbooks",r.id,o.v,"md",Yn.join(yo,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var yo,qS,hu,WS,lB,Vn,Fm,Oi,gu=j(()=>{rr();ue();sr();yo=process.env.BOSTRAT_STATE_DIR||Yn.join(process.env.HOME||iB.tmpdir(),".bostrat"),qS=Yn.join(yo,"runbooks.json"),hu=Yn.join(yo,"runbooks"),WS=256*1024,lB=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];Vn=null,Fm=null;Oi=e=>Promise.resolve(e).catch(()=>{})});import Hm from"node:fs/promises";import Ci from"node:path";import{randomUUID as mB}from"node:crypto";function qm(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return hB.has(t)?"image":wB.has(t)?"doc":null}function gB(e){let r=Ci.basename(String(e||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function oT(e,{filename:t,buffer:n}){if(!e)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>nT)throw new Error(`file too large \u2014 max ${Math.floor(nT/(1024*1024))} MB`);let r=gB(t),o=Ci.extname(r).slice(1),s=qm(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=mB().slice(0,8),i=Wm(e);await Hm.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Hm.writeFile(Ci.join(i,c),n,{mode:384}),await Hm.chmod(Ci.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${rT}/${c}`,kind:s,size:n.length}}function sT(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Km(e){return e.role==="user"&&yB.test((e.text||"").trim())}var rT,Wm,nT,hB,wB,yB,Gm=j(()=>{rT=".bostrat/uploads",Wm=e=>Ci.join(e,rT),nT=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,hB=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),wB=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);yB=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function ko(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var aT=j(()=>{});import{execFile as bB}from"node:child_process";import{promisify as kB}from"node:util";function Vm(e){return`refs/bostrat/wip/${e}`}function SB(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function lT(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function TB(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:vB}async function zm(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await Or(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Or(o,["remote","get-url","origin"])}catch{return iT.has(o)||(iT.add(o),t.log?.(`[wip-sync] ${r}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let s=await ws(o,"bostrat: wip snapshot"),{stdout:a}=await Or(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&yu.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=Vm(r);return await Or(o,["push","--force","origin",`${s}:${c}`],cT),yu.set(o,i),await O(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:c,sha:s,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${r}: pushed ${s.slice(0,7)} \u2192 ${c}`),{pushed:!0,sha:s,ref:c}}catch(s){return t.log?.(`[wip-sync] ${r}: push failed (will retry): ${String(s?.message||s).split(`
|
|
200
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
201
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
203
|
+
`)}`:t),this.name="MockRenderError"}},Io=null,Vm=null;xn=e=>wt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),vn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Pi(e,r,"video"):r&&Pi(e,r,n)||(n==="html"?Wt(e,t).html:null);return o?lr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Di=e=>ze.join(e,".bostrat","mocks");P1=Buffer.from([137,80,78,71,13,10,26,10]);W1=H1,zm=Promise.resolve()});import V1 from"node:fs/promises";import cT from"node:path";import z1 from"node:os";import{randomUUID as lT}from"node:crypto";async function Xn(){return Jn||(Zm||=(async()=>{let e=!0;try{Jn=JSON.parse(await V1.readFile(uT,"utf8"))}catch{Jn={flows:[]},e=!1}finally{Zm=null}if(Jn.flows||=[],!e&&Ne()){let{records:t,source:n}=await et("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Jn.flows.push(r.data);Jn.flows.length&&await Gs().catch(()=>{})}}return Jn})(),Zm)}async function Gs(){await Tt(uT,Jn)}function Qm(e,t,n={}){return $({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function dT({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>oT)throw new Error(`steps must be an array of 1-${oT}`);let r=await Bi({trackId:e}),o=[...n],s=[];for(let i=0;i<t.length;i++){let c=t[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>aT)throw new Error(`${l}: caption over ${aT} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>iT)throw new Error(`${l}: title over ${iT} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===d);if(p.length||(p=r.filter(S=>S.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let h=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(S=>S.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let g=o.findIndex(S=>S.mockId===h.id),k=g>=0?o.splice(g,1)[0].id:lT().slice(0,8);s.push({id:k,mockId:h.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function J1(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>sT)throw new Error(`name over ${sT} chars`);return e.trim()}async function fT({trackId:e}={}){let t=await Xn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function X1(e){let n=(await Xn()).flows.find(r=>r.id===e);return n?{...n}:null}async function pT(e){let t=await X1(e);if(!t)return null;let n=new Map((await Bi({trackId:t.trackId})).map(r=>[r.id,r]));return t.steps=t.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=tn(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),t}async function mT({trackId:e,name:t,steps:n,cwd:r,box:o,agent:s}){if(!e)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=J1(t),i=await Xn(),c=i.flows.find(p=>p.trackId===e&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await dT({trackId:e,steps:n}),f=Date.now(),d={id:lT().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await Gs(),await Qm(d,"created"),Eu(d),{...d}}async function hT(e,{steps:t,note:n}={}){let o=(await Xn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await dT({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await Gs(),await Qm(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Eu(o),{...o}}async function wT(e){let t=await Xn(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Gs(),await Qm(r,"deleted"),Vt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function gT(e){return(await Xn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function yT(e){let n=(await Xn()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),Eu(r);return n.length&&await Gs(),n.length}async function bT(e){let t=await Xn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Gs();for(let r of n)Vt("flows",r.id).catch(()=>{})}return n.length}async function kT(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Xn();for(let r of n.flows)await Eu(r);return{pushed:n.flows.length,source:"plane"}}var Y1,uT,oT,sT,aT,iT,Jn,Zm,Eu,eh=j(()=>{me();cr();Ks();ur();Y1=process.env.BOSTRAT_STATE_DIR||cT.join(process.env.HOME||z1.tmpdir(),".bostrat"),uT=cT.join(Y1,"flows.json"),oT=20,sT=80,aT=500,iT=80,Jn=null,Zm=null;Eu=e=>wt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import er from"node:fs/promises";import tr from"node:path";import Z1 from"node:os";import{randomUUID as Q1}from"node:crypto";function ji(e){return ej.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Tn(){return Zn||(th||=(async()=>{let e=!0;try{Zn=JSON.parse(await er.readFile(vT,"utf8"))}catch{Zn={runbooks:[]},e=!1}finally{th=null}return Zn.runbooks||=[],!e&&Ne()&&await tj().catch(()=>{}),Zn})(),th)}async function Ro(){await Tt(vT,Zn)}function _u(e){return Li(wt("runbooks",{...e},{trackId:e.trackId}))}async function tj(){let{records:e,source:t}=await et("runbooks");if(!(t!=="plane"||!e.length)){await er.mkdir(Au,{recursive:!0});for(let n of e){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await Pa("runbooks",r.id,o.v,"md");typeof s=="string"&&await er.writeFile(Qn(r,o.v),s).catch(()=>{})}Zn.runbooks.push(r)}Zn.runbooks.length&&await Ro()}}function Vs(e,t,n={}){return $({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Qn(e,t){return tr.join(Au,`${e.id}.v${t}.md`)}function ST(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function TT(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>xT)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${xT/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=ji(e);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return t}function nj(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function rj({trackId:e,status:t}={}){let n=await Tn(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function ET({trackId:e,status:t}={}){let n=await Tn(),r=await rj({trackId:e,status:t});if(!Ne())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await et("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function nh(e){let t=await oj(e);if(t)return t;if(!Ne())return null;let{records:n,source:r}=await et("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function AT(e,t){let n=await Pa("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function oj(e){let n=(await Tn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function _T({trackId:e,trackName:t,name:n,md:r,cwd:o,box:s,agent:a}){if(!e)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=TT(r),c=await Tn(),l={id:Q1().slice(0,8),trackId:e,trackName:t||e,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await er.mkdir(Au,{recursive:!0}),await er.writeFile(Qn(l,1),r),l.versions.push({v:1,mdRel:tr.relative($o,Qn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Ro(),await Vs(l,"created",{version:1}),_u(l),Li(lr("runbooks",l.id,1,"md",Qn(l,1))),{...l}}async function IT(e,{md:t,note:n}={}){let o=(await Tn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");nj(o,"update");let s=TT(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await er.mkdir(Au,{recursive:!0}),await er.writeFile(Qn(o,a),t),o.versions.push({v:a,mdRel:tr.relative($o,Qn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Ro(),await Vs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),_u(o),Li(lr("runbooks",o.id,a,"md",Qn(o,a))),{...o}}async function $T(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Tn()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await Ro(),await Vs(r,t==="done"?"done":"reopened"),_u(r),{...r})}async function RT(e){let t=await Tn(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await er.rm(tr.join($o,o.mdRel),{force:!0}).catch(()=>{});return await Ro(),r.trackDeletedAt||await Vs(r,"deleted"),Li(Vt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function OT(e){let n=(await Tn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Vs(r,"orphaned"),r.cwd=null,_u(r);return n.length&&await Ro(),n.length}async function CT(e){let t=await Tn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Vs(r,"deleted");for(let o of r.versions||[])await er.rm(tr.join($o,o.mdRel),{force:!0}).catch(()=>{});Li(Vt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Ro()),n.length}async function MT(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Tn();for(let r of n.runbooks){await wt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await lr("runbooks",r.id,o.v,"md",tr.join($o,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var $o,vT,Au,xT,ej,Zn,th,Li,Iu=j(()=>{cr();me();ur();$o=process.env.BOSTRAT_STATE_DIR||tr.join(process.env.HOME||Z1.tmpdir(),".bostrat"),vT=tr.join($o,"runbooks.json"),Au=tr.join($o,"runbooks"),xT=256*1024,ej=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];Zn=null,th=null;Li=e=>Promise.resolve(e).catch(()=>{})});import rh from"node:fs/promises";import Fi from"node:path";import{randomUUID as sj}from"node:crypto";function sh(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return aj.has(t)?"image":ij.has(t)?"doc":null}function cj(e){let r=Fi.basename(String(e||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function DT(e,{filename:t,buffer:n}){if(!e)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>NT)throw new Error(`file too large \u2014 max ${Math.floor(NT/(1024*1024))} MB`);let r=cj(t),o=Fi.extname(r).slice(1),s=sh(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=sj().slice(0,8),i=oh(e);await rh.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await rh.writeFile(Fi.join(i,c),n,{mode:384}),await rh.chmod(Fi.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${PT}/${c}`,kind:s,size:n.length}}function BT(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function ah(e){return e.role==="user"&&lj.test((e.text||"").trim())}var PT,oh,NT,aj,ij,lj,ih=j(()=>{PT=".bostrat/uploads",oh=e=>Fi.join(e,PT),NT=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,aj=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),ij=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);lj=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Oo(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var jT=j(()=>{});import{execFile as uj}from"node:child_process";import{promisify as dj}from"node:util";function ch(e){return`refs/bostrat/wip/${e}`}function mj(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function UT(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function hj(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:pj}async function lh(e,{log:t=console,force:n=!1}={}){let{id:r,cwd:o}=e??{};if(!r||!o)return{pushed:!1,reason:"no-workspace"};try{await jr(o,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await jr(o,["remote","get-url","origin"])}catch{return LT.has(o)||(LT.add(o),t.log?.(`[wip-sync] ${r}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let s=await As(o,"bostrat: wip snapshot"),{stdout:a}=await jr(o,["rev-parse",`${s}^{tree}`]),i=a.trim();if(!n&&$u.get(o)===i)return{pushed:!1,reason:"unchanged"};let c=ch(r);return await jr(o,["push","--force","origin",`${s}:${c}`],FT),$u.set(o,i),await $(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:c,sha:s,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${r}: pushed ${s.slice(0,7)} \u2192 ${c}`),{pushed:!0,sha:s,ref:c}}catch(s){return t.log?.(`[wip-sync] ${r}: push failed (will retry): ${String(s?.message||s).split(`
|
|
204
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function uh(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await jr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await jr(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await As(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await jr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if($u.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=mj(n);return await jr(r,["push","origin",`${o}:${i}`],FT),$u.set(r,a),await $(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:i,sha:o,rescue:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: rescued stranded work ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: rescue push failed: ${String(o?.message||o).split(`
|
|
205
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function HT({listTracks:e,sweepMs:t=hj(),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 lh(i,{log:n})}catch(a){n.log?.(`[wip-sync] sweep failed: ${a?.message||a}`)}finally{r=!1}}},s=setInterval(o,t);return s.unref?.(),o(),{stop:()=>clearInterval(s),cycle:o}}var fj,pj,FT,jr,$u,LT,dh=j(()=>{Pl();me();Bn();fj=dj(uj),pj=12e4,FT=6e4;jr=(e,t,n=15e3)=>fj("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),$u=new Map,LT=new Set});import wj from"node:fs";import fh from"node:fs/promises";import gj from"node:path";function xj(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function WT(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(!xj(t))return{restored:!1,reason:"plane not configured"};let s=ts(e),a=gj.join(s,`${o}.ndjson`);try{if(wj.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=Ie(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let d=new AbortController,p=setTimeout(()=>d.abort(),kj),h;try{h=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${yj}`,{headers:c,signal:d.signal})}finally{clearTimeout(p)}if(h.status===404)return{restored:!1,reason:"unknown to plane"};if(!h.ok)return{restored:!1,reason:`plane ${h.status}`};let w=await h.json();for(let g of w.events||[])l.push(JSON.stringify(g));if(l.length>bj)return{restored:!1,reason:"event count over safety cap"};if(w.next==null)break;u=w.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await fh.mkdir(s,{recursive:!0});let f=`${a}.pull-${process.pid}-${Date.now()}`;return await fh.writeFile(f,l.join(`
|
|
202
206
|
`)+`
|
|
203
|
-
`),await
|
|
207
|
+
`),await fh.rename(f,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var yj,bj,kj,qT=j(()=>{it();me();yj=1e3,bj=5e5,kj=3e4});var vE={};Pd(vE,{HANDOFF_LATEST_REL:()=>Co,KNOWN_AGENTS:()=>Xi,TRACK_NAME_MAX:()=>Eh,addTrackRepo:()=>Uh,adoptTrack:()=>sw,agentAvailability:()=>Mu,announceNodeUpdate:()=>An,answerFullscreenOffer:()=>Zh,answerPrompt:()=>Pu,archiveTrack:()=>Do,archiveTrackForMergedPr:()=>qn,augmentFirstPromptAfterHandoff:()=>eE,buildAgentCmd:()=>Ih,canResumeSubtask:()=>bE,cancelQueued:()=>ec,cancelQueuedToDraft:()=>Nh,cancelStopJob:()=>tc,cancelSubtask:()=>Ku,changedRepos:()=>_o,commitTransferOut:()=>Wu,createSetupTrack:()=>qh,createTrack:()=>ju,deleteTrack:()=>Gh,demoteTransferredTracks:()=>ow,ensureLeadEpochs:()=>Qs,forceMergeSubtask:()=>lw,getHandoffJob:()=>ra,getSubtaskJob:()=>SL,getTrack:()=>te,getTransferOutJob:()=>tw,healDeadLead:()=>Yi,interruptLead:()=>Bu,isArchiving:()=>pL,isHandoffNoteEcho:()=>xh,isInterruptEcho:()=>vh,isModelSwitchEcho:()=>Sh,leadCliDead:()=>zh,listSubtaskJobs:()=>jo,listTracks:()=>le,matchTranscriptsToBoxes:()=>rE,mergeNotifyMessage:()=>hE,navigatePrompt:()=>Mh,notifyTrackForMergedPr:()=>Kn,notifyTrackForResolvedPr:()=>Lu,offerFollowupsForMergedPr:()=>Bo,pendingOffers:()=>Oh,queueShipCompleteNotify:()=>Dh,queuedMessages:()=>Mo,reapArchivedOrphans:()=>Xh,reconcileDemotedTracks:()=>Jh,reconcileHandoffOutcome:()=>wE,reconcileInterruptedHandoffs:()=>ew,reconcileInterruptedSubtasks:()=>aw,reconcileNodeUpdateNotices:()=>Rh,reconcileSubtaskOutcome:()=>yE,refreshStandingInstructions:()=>Wh,refuseIfTransferred:()=>rw,rehydrateLead:()=>rc,removeTrackRepo:()=>Hh,renameTrack:()=>Yh,reposOf:()=>Dt,resolveTrackBox:()=>Ii,resolveTrackBoxByHookToken:()=>Qi,restartLead:()=>Uu,resumeAgentCmd:()=>$h,sayToLead:()=>Pt,sendOptionsForAgent:()=>pn,setTrackDraft:()=>Lh,setTrackModel:()=>Fh,startLeadHandoff:()=>Hu,startSpinUpView:()=>Bh,startSubtask:()=>cw,startTransferOut:()=>nw,stopJobActive:()=>aE,subtaskMessages:()=>Ch,trackMessages:()=>Nu,unarchiveTrack:()=>Fu});import z from"node:fs/promises";import J from"node:path";import{randomUUID as Po}from"node:crypto";import{execFile as vj}from"node:child_process";import{promisify as Sj}from"node:util";function Ih(e,{skipPermissions:t=!0}={}){let n=Cu[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 $h(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Cu[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&&!Jt(r,t)){let c=o&&Pe(o)?` --name ${o}`:"";return`${s.base} --session-id ${t}${c}${i}`}return`${s.base} --resume ${t}${i}`}case"cursor":return`${s.base} --continue${i}`;case"codex":return`${s.base} resume --last${i}`;default:return`${s.base}${i}`}}function na(e){let t=Cu[e];if(t)return process.env[t.envVar]||t.base}async function Mu({maxAgeMs:e=6e4}={}){if(Lr.value&&Date.now()-Lr.at<e)return Lr.value;let t=Lr.value,n=await Promise.all(Xi.map(async r=>{let o=await sl(na(r),{retries:[]});return o===is?[r,t?.[r]??!0]:[r,o===ol]}));return Lr.value=Object.fromEntries(n),Lr.at=Date.now(),Lr.value}async function Zi(e,t=!0){let n=Ih(e,{skipPermissions:t});if(await sl(n)===as){Lr.at=0;let r=n.split(/\s+/)[0],o=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":pf[r]?`Install it on this node: ${pf[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${e}" isn't available on this box (command not found: ${r}). ${o}`)}return n}function eE(e,t=Co){return`${e.trim()}
|
|
204
208
|
|
|
205
|
-
(Read ${t} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function
|
|
206
|
-
`;return Qm=Qm.then(()=>K.mkdir(yn,{recursive:!0})).then(()=>K.appendFile(MB,n)).catch(()=>{}),Qm}async function bn({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await K.readFile(eh,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await X(),u=0;for(let d of l.tracks)qe(d)||!d.leadBox||(ve(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await Q().catch(()=>{}),s==="available")try{await K.mkdir(J.dirname(eh),{recursive:!0}),await K.writeFile(eh,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function hh({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await X(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||LB(e,c)||(ve(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await Q().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function LB(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!0;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function ST(e){if(!e)return null;let t=typeof e=="string"?[e]:Array.isArray(e?.codes)&&e.codes.length?e.codes:e?.code?[e.code]:[],n=[...new Set(t.filter(s=>FB.has(s)))];if(!n.length)return null;let r=typeof e=="object"?e?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function TT(e,t,n,{kind:r}={}){let o=await fe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Js(o.agent);await ur({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Je(t,n,{...sn(o.agent,n),kind:r||"orchestration"});let a=await Ya({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await ee({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await X(),c=le(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);ve(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await Q()}async function UB(e){let t=J.join(Ko(e),`${e.id}.ndjson`),n;try{n=await K.stat(t)}catch{return null}let r=pT.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await F(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return pT.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function HB(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Jp(n.name,n.agent)}async function Dt(e,t=null){let n=t??new Map((await Ut()).map(c=>[c.name,c])),r=!!e.transferredTo,o=e.boxes.map(c=>{let l=r?null:n.get(c.name),u=Mr.has(c.state)?c.state:l?c.role==="lead"?"live":c.state:"stopped";return{...c,state:u,cwd:l?.cwd||c.cwd,agent:l?.agent||c.agent,sessionId:l?.sessionId||c.sessionId}}),[s,{working:a,prompted:i}]=await Promise.all([UB(e),HB(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:Fi.has(e.id)}}function AT(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-WB,f=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function ET(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(f=>f.box.sessionId),a=Fy(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=AT(u,a,f=>i.has(f));for(let f of d){let p=u.find(m=>m.name===f.box);p&&(p.sessionId=f.sessionId),await Oy(f.box,f.sessionId),n=!0}}return n}async function ie(){let e=await X();await ET(e)&&await Q();let t=new Map((await Ut()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Dt(n,t)))}async function ki(e,t){let n=await X();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function Ki(e){let t=wa(e);if(!t)return null;let n=await X();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&bg(o.hookTokenHash,t))return{track:r,box:o};return null}async function oe(e){let t=await X(),n=le(t,e);return n?(await ET(t)&&await Q(),Dt(n)):null}async function wh(e){let t=await F(e).catch(()=>[]),n;try{n=dr(t)}catch{return[]}return Jb(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function ih(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(qB[e.agent]||[]).some(n=>n.test(t))}function ch(e){let t=(e.text||"").trim();return t?KB.some(n=>n.test(t)):!1}async function Su(e,t={}){let n=await oe(e);if(!n)return null;let r=new Set([...n.events.filter(g=>g.type==="orchestration-prompt"&&g.prompt).map(g=>String(g.prompt).trim()),...n.events.filter(g=>g.type==="said"&&g.text).map(g=>String(g.text).trim())]),o=Ys(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=Sm(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Jv(n),...l],f={windows:[...yk(n),...bk(n,kl),...a,...u]},m=(await Promise.all(n.boxes.map(g=>lp(g,t)))).flat(),w=m.map(g=>{let _={kind:"message",...g};if(_.role==="assistant"&&(_.text||"").trim()&&_.tools?.length){let ne=ri(_.agent).cliTools,ae=_.tools.filter(Te=>!ne.has(Te));return{..._,tools:ae.length?ae:void 0}}return _}).map(g=>{if(g.role!=="user")return g;let _=gl(g.text);return _?_.kind==="suppress"?null:{...g,note:_,text:Fb(_)}:g}).filter(Boolean).filter(g=>!(g.role==="user"&&r.has((g.text||"").trim()))).filter(g=>!ih(g)).filter(g=>!ch(g)).filter(g=>!ah(g)).filter(g=>!Km(g)).filter(g=>g.role!=="assistant"||!!(g.text||"").trim()||!!g.images?.length).filter(g=>!kk(g,f)),y=w.filter(g=>g.failed&&g.text===Qr);if(y.length){let g=new Map;for(let _ of y){if(!g.has(_.box)){let Te=[...(await Oe(_.box,{lines:ze}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Ee=>Ee[1]);g.set(_.box,Te.length?Te[Te.length-1]:null)}let ne=g.get(_.box);ne&&(_.text=`\u25A0 ${ne}`)}}let x=await Promise.all(n.boxes.map(g=>Qk(g,t)));await xv(n,x.flat());let{agentTurnBoxes:T}=await bv(n,w);for(let g of n.boxes)T.has(g.name)&&g.cwd&&Fc(g.cwd);await z0(n),await nS(n,w).catch(()=>{});for(let g of n.boxes)g.agent==="codex"&&g.state==="live"&&await Sv(n,g),g.agent==="cursor"&&g.state==="live"&&await Iv(n,g);let I=await Promise.all(n.boxes.map(g=>Zk(g,t)));await kv(n,I.flat().filter(g=>!th(g.box,g.ts,u)));let $=await F(n),D=c&&i.viewId?i.viewId:null,S=ax($),A=dr($).filter(g=>!(g.kind==="message"&&(ih(g)||ch(g)||ah(g)||Km(g)))&&!(g.kind==="view-card"&&D&&g.viewId===D)),R=ix(A,S),E=A.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&!R.has(g)&&th(g.box,g.ts,S))),L=Wf($,E).map(g=>({kind:"command-batch",id:g.id,ts:g.pending?g.ts:(g.anchorTs??g.ts)-1,anchorTs:g.anchorTs,anchorBox:g.anchorBox,agent:g.agent,boxRole:g.boxRole,commands:g.commands,pending:g.pending})).filter(g=>!th(g.anchorBox,g.anchorTs??g.ts,u)),U=Vb(zb([...E,...L].filter(g=>g.ts!=null).sort((g,_)=>g.ts-_.ts))),se=VB(n),G=se.size?U.filter(g=>g.kind==="event"||!se.has(g.box??g.anchorBox)):U,C=n.boxes.find(g=>g.name===n.leadBox)||null,Z=C?await La(C.cwd,C.sessionId,{fallbackNewest:!1}):null;if(!Z&&C){let g=[...$].reverse().find(_=>_.type==="context.usage"&&_.boxId===C.name);g?.payload?.window&&(Z={pct:g.payload.pct??(g.payload.occupancy!=null?g.payload.occupancy/g.payload.window:null),occupancy:g.payload.occupancy??null,window:g.payload.window,windowKnown:!0,model:g.payload.model||null})}let Y=[...m].reverse().find(g=>g.role==="assistant"&&g.model),te=Y?.model||null,we=[...n.events||[]].reverse().find(g=>g.type==="model-change"),ce=Z?.model||te;we&&(!Y?.ts||we.at>=Y.ts)&&(ce=we.to);let V=C?await Oe(C.name,{lines:ze}).catch(()=>""):"",ge=C&&co(C.agent,V)?await $n(C.name).catch(()=>V):V,B=C?br(C,ge):null;if(B&&C){let g=Al(C,B,ge);if(g){let _=h0(C.name,B.promptId);_.action!=="give-up"&&(_.action==="send"&&(De(C.name,g.keys,{kind:"auto-reject"}).catch(()=>{}),_.first&&O(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{ts:Date.now(),promptId:B.promptId,rule:g.id}}).catch(()=>{})),B=null)}}!B&&C&&!as(C.agent,V)&&!vo(n.id).length&&(B=Uf(G,{leadBoxId:C.name,provider:C.agent,isAnswered:g=>$.some(_=>_.type==="prompt.answered"&&_.payload?.promptId===g)})),B&&J0(n,C,B);let ye=G.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&g.ask&&!String(g.text||"").trim()&&!g.images?.length)),_e=!!C&&Yl(C.name,C.agent,V)&&!B;C&&!C.readyAt&&(_e||B||V&&is(C.agent,V))&&(C.readyAt=Date.now(),await Q());let Ie=!!C&&!C.readyAt&&Date.now()-(n.createdAt||0)<GB;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&C&&is(C.agent,V)&&!B){let g=await X(),_=le(g,n.id);_?.setup&&!_.setup.connectedAt&&(_.setup.connectedAt=Date.now(),n.setup.connectedAt=_.setup.connectedAt,await O(_,{type:"provider.connected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{provider:C.agent,ts:_.setup.connectedAt}}).catch(()=>{}),await Q())}let b=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,h=null;if(c){let g=Date.now()-i.startedAt;if(i.viewId&&!_e){let _=iu(n.id);_&&(ve(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:_.finishedAt}),await Q())}else if(B||g>Vv&&!_e&&!i.viewId||g>vm){let _=B?"needs-input":g>vm?"timeout":"no-view";iu(n.id,{error:_})&&await O(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:C?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:_}}).catch(()=>{})}else h={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ye,context:Z?{pct:Z.pct,occupancy:Z.occupancy,window:Z.window,windowKnown:Z.windowKnown,model:Z.model}:null,models:{current:ce,options:U0(C?.agent)},pendingPrompt:B,working:_e,stopping:IT(n.id),leadStarting:Ie,handoff:b,view:h,queued:vo(n.id).filter(g=>g.kind!=="ship-notify"&&g.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function VB(e){let t=new Set;for(let n of e.boxes||[])n.role==="subtask"&&n.name&&t.add(n.name);for(let n of Object.values(e.subtasks||{}))n?.box&&t.add(n.box);return t}async function gh(e,t){let n=await oe(e);if(!n)return null;let r=Eo(e).find(d=>d.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await F(n),a=dr(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Wf(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await Oe(o,{lines:ze}).catch(()=>"");u=Yl(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Tu(e,{promptId:t,answers:n,text:r}={}){let o=le(await X(),e);if(!o)throw new Error(`track "${e}" not found`);Vi(e,"prompt-answer");let s=o.boxes.find(m=>m.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await fe(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await wp(s);if(i||(i=await zB(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let m=typeof r=="string"?r.trim():"";if(!m)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(m)||m.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Je(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await De(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let m=qb(i,n||[],typeof r=="string"?r:"");if(!m)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Bt(o.id,m,{kind:"prompt-answer"}),c={mode:"say",value:m,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let m=d0(i,n||[]);await De(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=mb(i,n||[]);await Je(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:m,count:1}}if(i.answerMode==="say"){let m=i.questions||[];for(let w=0;w<m.length;w++){let y=m[w],x=new Set(Array.isArray(n?.[w])?n[w]:[]),T=[...x].map($=>y.options?.[$]).filter(Boolean).map($=>({label:$.label,description:$.description||""})),I=(y.options||[]).map(($,D)=>x.has(D)?null:$?.label).filter(Boolean);Mp(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??m.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:T,rejected:I})}return await ee({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),d=[...u].map(m=>l.options?.[m]).filter(Boolean).map(m=>({label:m.label,description:m.description||""})),f=(l.options||[]).map((m,w)=>u.has(w)?null:m?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:d,rejected:f,...p?{control:p}:{}},await ee({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Mp(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function zB(e,t){if(!t)return null;let n=await F(e).catch(()=>[]);return Uf(dr(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function yh(e,{promptId:t,questionIndex:n}={}){let r=le(await X(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await fe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await wp(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(t&&t!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=f0(a,n);return c.length?(await De(r.leadBox,c,{kind:"navigate-prompt"}),await ee({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Au(e){let t=Di.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Di.set(e,t)),t}function vo(e){return(Di.get(e)?.items??[]).map(t=>({...t}))}function Gi(e){let t=Di.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function bh(e){let t=Gi(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=le(await X(),e);if(o)return r=Ks(o,n),await Q(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function kh(e){let t=Au(e);t.timer||(t.timer=setInterval(()=>YB(e).catch(()=>{}),JB))}async function YB(e){let t=Di.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=le(await X(),e);if(!n||n.archivedAt){Gi(e);return}let r=Ys(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){ko(t,!1);return}let o=await fe(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await Ch(o)&&(o=await Ui(n,s,"queue")?await fe(n.leadBox):null,await Q()),!o){ko(t,!1);return}if(uo(s?.agent)){ko(t,!1);return}let a=await Oe(n.leadBox,{lines:ze}).catch(()=>"");if(ll(s?.agent,a)){ko(t,!1);return}if(co(s?.agent,a)){let c=await $n(n.leadBox).catch(()=>a);if(br(s,c)){ko(t,!1);return}}if(!ko(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await lh(n,s,o,i.text,i.kind??null,i.attachments??null)}catch(c){if(c?.code!=="cli-not-running")throw c;t.items.unshift(i),await Ui(n,s,"queue-gate")}await Q()}finally{t.flushing=!1}}}async function _T(e,t,n,r){let o=uo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Ks(e,[n]),await Q()),O0(e.id,o))try{await O(e,{type:"usage.cap.hit",actor:"system",provider:t?.agent??null,boxId:e.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${t?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function XB(e,t){let n=await Oe(e.leadBox,{lines:ze}).catch(()=>"");if(!co(t?.agent,n))return!1;let r=await $n(e.leadBox).catch(()=>n);return!!br(t,r)}async function lh(e,t,n,r,o=null,s=null){await _T(e,t,r,o);let a=Bi.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=vT(r,e.pendingHandoffHydrate.artifact||xo),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(u.length){let f=sT(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
209
|
+
(Read ${t} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function xh(e){return e.role==="user"&&Aj.test((e.text||"").trim())}async function Z(){return qt||(ph||=(async()=>{try{qt=JSON.parse(await z.readFile(ZT,"utf8"))}catch{qt={tracks:[]}}finally{ph=null}return qt})(),ph)}async function ee(){await Tt(ZT,qt)}function $j(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
210
|
+
`;return mh=mh.then(()=>z.mkdir(En,{recursive:!0})).then(()=>z.appendFile(Tj,n)).catch(()=>{}),mh}async function An({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let f=null;try{f=JSON.parse(await z.readFile(hh,"utf8"))}catch{}if(f?.latest===t&&f?.current===e)return{announced:0,deduped:!0}}let l=await Z(),u=0;for(let f of l.tracks)Ve(f)||!f.leadBox||(Te(f,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ee().catch(()=>{}),s==="available")try{await z.mkdir(J.dirname(hh),{recursive:!0}),await z.writeFile(hh,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Rh({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await Z(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||Rj(e,c)||(Te(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ee().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function Rj(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!0;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function tE(e){if(!e)return null;let t=typeof e=="string"?[e]:Array.isArray(e?.codes)&&e.codes.length?e.codes:e?.code?[e.code]:[],n=[...new Set(t.filter(s=>Oj.has(s)))];if(!n.length)return null;let r=typeof e=="object"?e?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function nE(e,t,n,{kind:r}={}){let o=await ge(t);if(!o)throw new Error(`box "${t}" is not running`);let s=na(o.agent);await hr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Ze(t,n,{...pn(o.agent,n),kind:r||"orchestration"});let a=await ni({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await re({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await Z(),c=he(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);Te(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await ee()}async function Cj(e){let t=J.join(ts(e),`${e.id}.ndjson`),n;try{n=await z.stat(t)}catch{return null}let r=KT.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await F(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return KT.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Mj(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:sm(n.name,n.agent)}async function Mt(e,t=null){let n=t??new Map((await zt()).map(c=>[c.name,c])),r=!!e.transferredTo,o=e.boxes.map(c=>{let l=r?null:n.get(c.name),u=Fr.has(c.state)?c.state:l?c.role==="lead"?"live":c.state:"stopped";return{...c,state:u,cwd:l?.cwd||c.cwd,agent:l?.agent||c.agent,sessionId:l?.sessionId||c.sessionId}}),[s,{working:a,prompted:i}]=await Promise.all([Cj(e),Mj(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:zi.has(e.id)}}function rE(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),f=l.createdAt-Nj,d=r[u+1]?.createdAt??1/0,p=o.find(h=>!s.has(h.id)&&h.birthMs>=f&&h.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function oE(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(d=>d.box.sessionId),a=nb(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=rE(u,a,d=>i.has(d));for(let d of f){let p=u.find(h=>h.name===d.box);p&&(p.sessionId=d.sessionId),await Vy(d.box,d.sessionId),n=!0}}return n}async function le(){let e=await Z();await oE(e)&&await ee();let t=new Map((await zt()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Mt(n,t)))}async function Ii(e,t){let n=await Z();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function Qi(e){let t=va(e);if(!t)return null;let n=await Z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Pg(o.hookTokenHash,t))return{track:r,box:o};return null}async function te(e){let t=await Z(),n=he(t,e);return n?(await oE(t)&&await ee(),Mt(n)):null}async function Oh(e){let t=await F(e).catch(()=>[]),n;try{n=wr(t)}catch{return[]}return dk(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function vh(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(Pj[e.agent]||[]).some(n=>n.test(t))}function Sh(e){let t=(e.text||"").trim();return t?Dj.some(n=>n.test(t)):!1}async function Nu(e,t={}){let n=await te(e);if(!n)return null;let r=new Set([...n.events.filter(A=>A.type==="orchestration-prompt"&&A.prompt).map(A=>String(A.prompt).trim()),...n.events.filter(A=>A.type==="said"&&A.text).map(A=>String(A.text).trim())]),o=ra(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=jm(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[..._S(n),...l],d={windows:[...Nk(n),...Pk(n,Il),...a,...u]},h=(await Promise.all(n.boxes.map(A=>yp(A,t)))).flat(),w=h.map(A=>{let G={kind:"message",...A};if(G.role==="assistant"&&(G.text||"").trim()&&G.tools?.length){let Ge=li(G.agent).cliTools,_t=G.tools.filter(at=>!Ge.has(at));return{...G,tools:_t.length?_t:void 0}}return G}).map(A=>{if(A.role!=="user")return A;let G=El(A.text);return G?G.kind==="suppress"?null:{...A,note:G,text:nk(G)}:A}).filter(Boolean).filter(A=>!(A.role==="user"&&r.has((A.text||"").trim()))).filter(A=>!vh(A)).filter(A=>!Sh(A)).filter(A=>!xh(A)).filter(A=>!ah(A)).filter(A=>A.role!=="assistant"||!!(A.text||"").trim()||!!A.images?.length).filter(A=>!Dk(A,d)),g=w.filter(A=>A.failed&&A.text===ao);if(g.length){let A=new Map;for(let G of g){if(!A.has(G.box)){let at=[...(await Ce(G.box,{lines:Xe}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(nt=>nt[1]);A.set(G.box,at.length?at[at.length-1]:null)}let Ge=A.get(G.box);Ge&&(G.text=`\u25A0 ${Ge}`)}}let k=await Promise.all(n.boxes.map(A=>h0(A,t)));await Qv(n,k.flat());let{agentTurnBoxes:S}=await Xv(n,w);for(let A of n.boxes)S.has(A.name)&&A.cwd&&zc(A.cwd);await ux(n),await NS(n,w).catch(()=>{});for(let A of n.boxes)A.agent==="codex"&&A.state==="live"&&await tS(n,A),A.agent==="cursor"&&A.state==="live"&&await aS(n,A);let E=await Promise.all(n.boxes.map(A=>m0(A,t)));await Zv(n,E.flat().filter(A=>!wh(A.box,A.ts,u)));let R=await F(n),B=c&&i.viewId?i.viewId:null,T=vx(R),_=wr(R).filter(A=>!(A.kind==="message"&&(vh(A)||Sh(A)||xh(A)||ah(A)))&&!(A.kind==="view-card"&&B&&A.viewId===B)),O=Sx(_,T),I=_.filter(A=>!(A.kind==="message"&&A.role==="assistant"&&!O.has(A)&&wh(A.box,A.ts,T))),U=Zf(R,I).map(A=>({kind:"command-batch",id:A.id,ts:A.pending?A.ts:(A.anchorTs??A.ts)-1,anchorTs:A.anchorTs,anchorBox:A.anchorBox,agent:A.agent,boxRole:A.boxRole,commands:A.commands,pending:A.pending})).filter(A=>!wh(A.anchorBox,A.anchorTs??A.ts,u)),H=lk(uk([...I,...U].filter(A=>A.ts!=null).sort((A,G)=>A.ts-G.ts))),ae=jj(n),X=ae.size?H.filter(A=>A.kind==="event"||!ae.has(A.box??A.anchorBox)):H,C=n.boxes.find(A=>A.name===n.leadBox)||null,Q=C?await Ka(C.cwd,C.sessionId,{fallbackNewest:!1}):null;if(!Q&&C){let A=[...R].reverse().find(G=>G.type==="context.usage"&&G.boxId===C.name);A?.payload?.window&&(Q={pct:A.payload.pct??(A.payload.occupancy!=null?A.payload.occupancy/A.payload.window:null),occupancy:A.payload.occupancy??null,window:A.payload.window,windowKnown:!0,model:A.payload.model||null})}let ve=[...h].reverse().find(A=>A.role==="assistant"&&A.model),W=ve?.model||null,be=[...n.events||[]].reverse().find(A=>A.type==="model-change"),se=Q?.model||W;be&&(!ve?.ts||be.at>=ve.ts)&&(se=be.to);let pe=C?await Ce(C.name,{lines:Xe}).catch(()=>""):"",ie=C&&wo(C.agent,pe)?await Nn(C.name).catch(()=>pe):pe,L=C?Tr(C,ie):null;if(L&&C){let A=Ml(C,L,ie);if(A){let G=O0(C.name,L.promptId);G.action!=="give-up"&&(G.action==="send"&&(Le(C.name,A.keys,{kind:"auto-reject"}).catch(()=>{}),G.first&&$(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{ts:Date.now(),promptId:L.promptId,rule:A.id}}).catch(()=>{})),L=null)}}!L&&C&&!ws(C.agent,pe)&&!Mo(n.id).length&&(L=Jf(X,{leadBoxId:C.name,provider:C.agent,isAnswered:A=>R.some(G=>G.type==="prompt.answered"&&G.payload?.promptId===A)})),L&&dx(n,C,L);let ce=X.filter(A=>!(A.kind==="message"&&A.role==="assistant"&&A.ask&&!String(A.text||"").trim()&&!A.images?.length)),Ae=!!C&&su(C.name,C.agent,pe)&&!L;C&&!C.readyAt&&(Ae||L||pe&&gs(C.agent,pe))&&(C.readyAt=Date.now(),await ee());let _e=!!C&&!C.readyAt&&Date.now()-(n.createdAt||0)<Bj;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&C&&gs(C.agent,pe)&&!L){let A=await Z(),G=he(A,n.id);G?.setup&&!G.setup.connectedAt&&(G.setup.connectedAt=Date.now(),n.setup.connectedAt=G.setup.connectedAt,await $(G,{type:"provider.connected",actor:"system",source:"pane",boxId:C.name,provider:C.agent,payload:{provider:C.agent,ts:G.setup.connectedAt}}).catch(()=>{}),await ee())}let xt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,je=null;if(c){let A=Date.now()-i.startedAt;if(i.viewId&&!Ae){let G=yu(n.id);G&&(Te(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:G.finishedAt}),await ee())}else if(L||A>ES&&!Ae&&!i.viewId||A>Bm){let G=L?"needs-input":A>Bm?"timeout":"no-view";yu(n.id,{error:G})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:C?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:G}}).catch(()=>{})}else je={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ce,context:Q?{pct:Q.pct,occupancy:Q.occupancy,window:Q.window,windowKnown:Q.windowKnown,model:Q.model}:null,models:{current:se,options:rx(C?.agent)},pendingPrompt:L,working:Ae,stopping:aE(n.id),leadStarting:_e,handoff:xt,view:je,queued:Mo(n.id).filter(A=>A.kind!=="ship-notify"&&A.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function jj(e){let t=new Set;for(let n of e.boxes||[])n.role==="subtask"&&n.name&&t.add(n.name);for(let n of Object.values(e.subtasks||{}))n?.box&&t.add(n.box);return t}async function Ch(e,t){let n=await te(e);if(!n)return null;let r=jo(e).find(f=>f.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await F(n),a=wr(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Zf(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await Ce(o,{lines:Xe}).catch(()=>"");u=su(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Pu(e,{promptId:t,answers:n,text:r}={}){let o=he(await Z(),e);if(!o)throw new Error(`track "${e}" not found`);tc(e,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await ge(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Ep(s);if(i||(i=await Lj(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let h=typeof r=="string"?r.trim():"";if(!h)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(h)||h.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Ze(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Le(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=ak(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Pt(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=_0(i,n||[]);await Le(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=Rb(i,n||[]);await Ze(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let g=h[w],k=new Set(Array.isArray(n?.[w])?n[w]:[]),S=[...k].map(R=>g.options?.[R]).filter(Boolean).map(R=>({label:R.label,description:R.description||""})),E=(g.options||[]).map((R,B)=>k.has(B)?null:R?.label).filter(Boolean);Hp(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:S,rejected:E})}return await re({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),d=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:f,rejected:d,...p?{control:p}:{}},await re({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Hp(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function Lj(e,t){if(!t)return null;let n=await F(e).catch(()=>[]);return Jf(wr(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Mh(e,{promptId:t,questionIndex:n}={}){let r=he(await Z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await ge(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Ep(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(t&&t!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=I0(a,n);return c.length?(await Le(r.leadBox,c,{kind:"navigate-prompt"}),await re({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Du(e){let t=qi.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},qi.set(e,t)),t}function Mo(e){return(qi.get(e)?.items??[]).map(t=>({...t}))}function ec(e){let t=qi.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function Nh(e){let t=ec(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=he(await Z(),e);if(o)return r=Zs(o,n),await ee(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Ph(e){let t=Du(e);t.timer||(t.timer=setInterval(()=>Uj(e).catch(()=>{}),Fj))}async function Uj(e){let t=qi.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=he(await Z(),e);if(!n||n.archivedAt){ec(e);return}let r=ra(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Oo(t,!1);return}let o=await ge(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await zh(o)&&(o=await Yi(n,s,"queue")?await ge(n.leadBox):null,await ee()),!o){Oo(t,!1);return}if(yo(s?.agent)){Oo(t,!1);return}let a=await Ce(n.leadBox,{lines:Xe}).catch(()=>"");if(gl(s?.agent,a)){Oo(t,!1);return}if(wo(s?.agent,a)){let c=await Nn(n.leadBox).catch(()=>a);if(Tr(s,c)){Oo(t,!1);return}}if(!Oo(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await Th(n,s,o,i.text,i.kind??null,i.attachments??null)}catch(c){if(c?.code!=="cli-not-running")throw c;t.items.unshift(i),await Yi(n,s,"queue-gate")}await ee()}finally{t.flushing=!1}}}async function sE(e,t,n,r){let o=yo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Zs(e,[n]),await ee()),V0(e.id,o))try{await $(e,{type:"usage.cap.hit",actor:"system",provider:t?.agent??null,boxId:e.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${t?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function Hj(e,t){let n=await Ce(e.leadBox,{lines:Xe}).catch(()=>"");if(!wo(t?.agent,n))return!1;let r=await Nn(e.leadBox).catch(()=>n);return!!Tr(t,r)}async function Th(e,t,n,r,o=null,s=null){await sE(e,t,r,o);let a=Ki.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=eE(r,e.pendingHandoffHydrate.artifact||Co),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=BT(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
207
211
|
|
|
208
|
-
${
|
|
212
|
+
${d}`:d}let f=Te(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||Xs(e,null);try{if(await hr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:na(n.agent),bailOnDialog:!0}),(Ki.get(e.id)||0)!==a){!o&&r.trim()&&(Zs(e,[r]),await ee());let d=new Error("delivery stopped \u2014 the message was handed back as the draft");throw d.interrupted=!0,d}if(await Hj(e,t)){let d=Du(e.id),p={id:Po(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};d.items.unshift(p),Ph(e.id);let h=new Error("lead is blocked on a prompt \u2014 message queued");throw h.requeued={id:p.id,position:d.items.length},h}await Ze(e.leadBox,i,{...pn(t?.agent,i),kind:c?"lead-handoff-first-prompt":"say"})}catch(d){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),Te(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,...d?.interrupted?{reason:"interrupted"}:{},...d?.requeued?{reason:"requeued"}:{}}),d?.requeued)return{queued:!0,...d.requeued};throw d}await nL(e,t,i)||(await iE(e.leadBox,t?.agent,i.length+8,i).catch(()=>{}),Te(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,reason:"unsubmitted"}),o||Zs(e,[r])),c&&await re({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Dh(e,{actions:t}={}){Du(e).items.push({id:Po(),text:Tx(t),ts:Date.now(),kind:"ship-notify"}),Ph(e)}async function Pt(e,t,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(p=>p&&typeof p.rel=="string"):[];if((typeof t!="string"||!Ts(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=he(await Z(),e);if(!s)throw new Error(`track "${e}" not found`);if(s.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);rw(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=Ts(t);await sE(s,a,i,n),tc(e,"say");let c=await ge(s.leadBox);if(c&&await zh(c)?await Yi(s,a,"say")?(await ee(),c=await ge(s.leadBox)):c=null:!c&&await rc(s)&&(await ee(),c=await ge(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Du(e),u=await Ce(s.leadBox,{lines:Xe}).catch(()=>""),f=l.items.length>0||gl(a?.agent,u);if(!f&&wo(a?.agent,u)){let p=await Nn(s.leadBox).catch(()=>u);f=!!Tr(a,p)}if(f){let p={id:Po(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),Ph(e),n||Xs(s,null),await ee(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let d;try{d=await Th(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await Yi(s,a,"say-gate")||(await ee(),c=await ge(s.leadBox),!c))throw p;d=await Th(s,a,c,i,n,o)}return await ee(),d?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:d.position}:{ok:!0,leadBox:s.leadBox}}async function Bh(e,{hint:t}={}){let n=he(await Z(),e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it first`);let r=jm(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";AS(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${GT}
|
|
209
213
|
|
|
210
|
-
What to preview: ${String(t).trim()}`:
|
|
214
|
+
What to preview: ${String(t).trim()}`:GT;try{await Pt(e,a,{kind:"spin-up-view"})}catch(i){throw yu(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Bu(e){let t=he(await Z(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);Ki.set(e,(Ki.get(e)||0)+1);let n=ec(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Js.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Zs(t,r),await ee()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=zj(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,No(Wj)]);let a=he(await Z(),e)||t;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}function aE(e){let t=Js.get(e);return!!t&&!t.canceled}function tc(e,t){let n=Js.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function jh(e){tc(e,"track-ended"),Ki.delete(e),ec(e)}function zj(e,{droppedCount:t,droppedTexts:n}){let r={startedAt:Date.now(),clicks:1,escSends:0,bgSends:0,queuedTexts:[...n],canceled:!1,cancelReason:null,result:null,restored:null},o;return r.settled=new Promise(s=>{o=s}),Js.set(e,r),(async()=>{let s=he(await Z(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await ge(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<qj&&!r.canceled;g++){await Le(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of Kj){await No(k);let S=await Ce(s.leadBox,{lines:Xe}).catch(()=>"");if(!ws(a?.agent,S)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<Vj&&!r.canceled&&(await No(Gj),!r.canceled);g++){let k=await Ce(s.leadBox,{lines:Xe}).catch(()=>"");if(!ws(a?.agent,k)){c=!0;break}await Le(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=he(await Z(),e)||s,f=u?.boxes.find(g=>g.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await Zj(u,f).catch(()=>null),d||(p=await tL(u,f).catch(()=>null)));let h=u&&i&&!r.canceled?await Jj(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=Zs(u,w),await ee()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await Yj(u||s,f,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!h,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await re({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!h}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),Js.get(e)===r&&Js.delete(e),o()}),r}async function Yj(e,t,n){await $(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function Jj(e){let t=vu(await F(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await $(e,{type:n?"pr.exists.answered":"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.box,payload:{ts:Date.now(),offerId:t.offerId,...n?{choice:"dismiss"}:{action:"dismiss",actions:null},reason:"interrupt"}}),t.offerId}async function Zj(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 Xj){await No(r);let o=await Ce(e.leadBox,{lines:Xe}).catch(()=>""),s=ei(t.agent,o);if(s&&yl(n.text,s))return await iE(e.leadBox,t.agent,n.text.length+8),Te(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function tL(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>Qj||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!We(r.text))return null;for(let s of eL){await No(s);let a;try{a=await yp(t,{})}catch{return null}if(Lb(a,r))return null;let i=await Ce(e.leadBox,{lines:Xe}).catch(()=>"");if(ei(t.agent,i))return null}return Te(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function nL(e,t,n){return ni({sessionName:e.leadBox,agent:t?.agent,agentCmd:na(t?.agent),text:n,trackId:e.id,flow:"say"})}async function iE(e,t,n,r=null){for(let o=0;o<3;o++){await ls(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await No(200);let s=await Ce(e,{lines:Xe}).catch(()=>""),a=ei(t,s);if(!a||r&&!yl(r,a)&&!(t==="codex"&&Ff(r,a)))return!0;n=a.length+8}return!1}function Xs(e,t,{source:n}={}){e.draftRev=(e.draftRev||0)+1;let r=typeof t=="string"?t:"";return r.trim()?e.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete e.draft,e.draft||null}function Zs(e,t){let n=[],r=o=>n.some(s=>We(s).includes(We(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?Xs(e,n.join(`
|
|
211
215
|
|
|
212
|
-
`),{source:"restore"})?.text??null:null}function
|
|
216
|
+
`),{source:"restore"})?.text??null:null}function oL(e,t){let n=We(t);if(!n)return!1;if(Mo(e.id).some(o=>We(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>rL)break;if(!(s.type!=="said"||!s.text||We(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Lh(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=he(await Z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=Ts(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&oL(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;We(o).includes(We(i))?Xs(r,o):Xs(r,`${i}
|
|
213
217
|
|
|
214
|
-
${o}`,{source:"restore"})}else
|
|
215
|
-
`);return`${
|
|
218
|
+
${o}`,{source:"restore"})}else Xs(r,o);return await ee(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Fh(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=he(await Z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await ge(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(f=>f.name===r.leadBox)?.agent||o.agent||"claude",i=ox(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Ka(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await hr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:na(o.agent)});let u=await sx(a,r.leadBox,i);return Te(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ee(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function cE(e){for(let t of e){if(!t||!await Es(t))continue;let n=await P0(t).catch(()=>null);if(!n)continue;if((await z.stat(J.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Dt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:J.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function _o(e){let t=[];for(let n of Dt(e?.workspace)){if(!n?.dir)continue;let r=await ui(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await di(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function lE(e){let t=e;typeof t=="string"&&!J.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Zo(t)??await Fc(t));let n=await cE([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 sL(e,t,n,r){let o=`track/${iw(t)}-${e}`,s=[];try{await z.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await lE(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Se(c).catch(()=>null),u=J.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${J.basename(c)}-${p}`;a.add(u);let f=J.join(n,u),d=await Is(c);await _s(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await nc(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Rt(i.base,i.dir).catch(()=>{});throw await z.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Uh(e,t){let n=await Z(),r=he(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 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 lE(typeof t=="string"?t.trim():t);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Se(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=J.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${J.basename(s)}-${d}`;let l=J.join(o.dir,c),u=await Is(s);await D0(s,o.branch)?await Nl(s,l,o.branch):await _s(s,l,o.branch,u);let f={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),f],await nc(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"add",repo:a??s,name:c,workspace:uE(o)}),await ee(),f}async function Hh(e,t){let n=await Z(),r=he(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 Nt.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 ui(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await di(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 Rt(s.base,s.dir).catch(()=>{}),await z.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await nc(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:uE(o)}),await ee(),{removed:s.name}}async function aL(e,t,{repo:n,repos:r,cwd:o}={}){await z.mkdir(Hi,{recursive:!0});let s=J.join(Hi,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return sL(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!J.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Zo(n)??await Fc(n));let i=await cE([n,o,Ej]);if(i){let c=`track/${iw(t)}-${e}`;try{let l=await Is(i);await _s(i,s,c,l),await Vi(s);let u=await Se(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await z.mkdir(s,{recursive:!0}),await Cp(s),await Vi(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Vi(e){try{await z.writeFile(J.join(e,"AGENTS.md"),Ui,{flag:"wx"})}catch{return}await uL(e,["/AGENTS.md"])}function lL(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
219
|
+
`);return`${cL}
|
|
216
220
|
|
|
217
221
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
218
222
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -224,20 +228,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
224
228
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
225
229
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
226
230
|
or preview an app, run it from that app's own repo directory.
|
|
227
|
-
`}async function
|
|
228
|
-
`).includes(s));o.length&&await
|
|
231
|
+
`}async function nc(e,t,n){let r=lL(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await z.writeFile(J.join(e,o),r).catch(()=>{})}async function fE(e){let{stdout:t}=await XT("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return J.resolve(e,t.trim())}async function uL(e,t){try{let n=await fE(e);await z.mkdir(J.dirname(n),{recursive:!0});let r=await z.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
232
|
+
`).includes(s));o.length&&await z.appendFile(n,(r.endsWith(`
|
|
229
233
|
`)||!r?"":`
|
|
230
234
|
`)+o.join(`
|
|
231
235
|
`)+`
|
|
232
|
-
`)}catch{}}async function
|
|
236
|
+
`)}catch{}}async function dL(e,t){try{let n=await fE(e),r=await z.readFile(n,"utf8"),o=r.split(`
|
|
233
237
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
234
|
-
`).length&&await
|
|
235
|
-
`))}catch{}}async function
|
|
238
|
+
`).length&&await z.writeFile(n,o.join(`
|
|
239
|
+
`))}catch{}}async function Wh(){let e={refreshed:0,removed:0,codexGlobal:null};try{await z.mkdir(En,{recursive:!0}),await z.writeFile(J.join(En,"CLAUDE.md"),iL).catch(()=>{}),await z.writeFile(J.join(En,"AGENTS.md"),Ui).catch(()=>{});let t=process.env.CODEX_HOME||J.join(process.env.HOME||"/tmp",".codex");if((await z.stat(t).catch(()=>null))?.isDirectory()){let r=J.join(t,"AGENTS.md"),o=await z.readFile(r,"utf8").catch(()=>null);o===null?(await z.writeFile(r,yh,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):gh(o)&&o!==yh&&(await z.writeFile(r,yh).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Hi,ta]){let o=await z.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=J.join(r,s.name),i=J.join(a,"AGENTS.md"),c=await z.readFile(i,"utf8").catch(()=>null);c!==null&&gh(c)&&c!==Ui&&(await z.writeFile(i,Ui).catch(()=>{}),e.refreshed++);let l=J.join(a,"CLAUDE.md"),u=await z.readFile(l,"utf8").catch(()=>null);u!==null&&gh(u)&&(await z.rm(l,{force:!0}).catch(()=>{}),await dL(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function pE(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Dt(t))n.base&&n.dir&&await Rt(n.base,n.dir).catch(()=>{});await z.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await Rt(t.base,t.dir):await z.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function ju({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 Zi(o,s),i=await Z(),c=Po().slice(0,8),l=await aL(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(h=>(h.boxes||[]).map(w=>w.name))),f=`${e.trim()} lead`;u.has(al(f))&&(f=`${e.trim().slice(0,50).trim()} ${c} lead`);let d=await pr(f,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0}),p={id:c,name:e.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Qh(d,"lead","live")],events:[],createdAt:Date.now()};return Te(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:h,base:w,baseRef:g,repo:k})=>({name:h,base:w,baseRef:g,repo:k}))}:{}}}),i.tracks.push(p),await ee(),Xa({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a,onReady:async()=>{let h=p.boxes.find(w=>w.name===p.leadBox);!h||h.readyAt||(h.readyAt=Date.now(),await ee())}}),Mt(p)}async function qh({provider:e="claude",fresh:t=!1}={}){if(!fL.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await Z()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Mt(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await Do(r.id).catch(()=>{})}let o=(await Zi(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Po().slice(0,8),a=J.join(En,"setup",s);await z.mkdir(a,{recursive:!0});let i=await pr(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&Xa({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:[Qh(i,"lead","live")],events:[],createdAt:Date.now()};return Te(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await Z()).tracks.push(c),await ee(),Mt(c)}async function Kh(e,t,n){await Zd(t.cwd).catch(()=>{}),await MS(e).catch(()=>{});let r=ea.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),ea.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of jo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await uw(a).catch(()=>{}),Nt.delete(a.id);for(let a of Object.values(t.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await Z(),s=new Set((o.tracks||[]).filter(a=>a.id!==e&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of t.boxes)s.has(a.name)||await Yt(a.name).catch(()=>{})}async function Gh(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await Z(),o=r.tracks.findIndex(a=>a.id===e);if(o===-1)throw new Error(`track "${e}" not found`);let s=r.tracks[o];return t||await tT(s),jh(e),await Kh(e,s,"track deleted"),n?await CT(e).catch(()=>{}):await OT(e).catch(()=>{}),await cx(),await pE(s),t&&await nT(e).catch(()=>{}),t?await bT(e).catch(()=>{}):await yT(s).catch(()=>{}),Te(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await ee(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Do(e){let t=await Z(),n=he(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(),jh(e),Te(n,"archived",{boxes:n.boxes.length}),await ee(),n.kind==="setup"?(await mE(e),Mt(n)):(zi.add(e),hL(e).catch(()=>{}).finally(()=>zi.delete(e)),{...await Mt(n),archiving:!0})}async function hL(e){try{await wL(e)}catch{}await mE(e).catch(()=>{})}async function mE(e){let t=await Z(),n=he(t,e);if(!(!n||!n.archivedAt)){await Kh(e,n,"track archived");for(let r of n.boxes)Fr.has(r.state)||(r.state="stopped");await ee()}}async function wL(e){let t=await Z(),n=he(t,e);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await ge(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Ck(n.leadBox,i.cwd,{timeoutMs:mL})),s="lead-cli"}catch{}if(r||(r=Vk(n,await F(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await $(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await ee()}async function qn(e,t){let n=await tt(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Vh(n);if(r.length){for(let o of r)await ko(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Do(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Vh(e){return(await xo().catch(()=>[])).filter(n=>n.trackId===e.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===e.repo&&n.number===e.number))}async function Kn(e,t){return Lu(e,t,"merged")}async function Bo(e,t){let n=await tt(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 Vh(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await Px(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=he(await Z(),n.trackId);if(!a)return null;let i=s[0];return Ai(Mt(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 Lu(e,t,n="merged"){let r=await tt(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 Vh(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Nx(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await Pt(r.trackId,hE(a),{kind:"merge-notify"}),!0}catch{return!1}}function hE(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 ${VT[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 ${VT[i.outcome]??i.outcome}`).join(`
|
|
236
240
|
`),a=n.length?r.length?`This is an automated merge notification: pick up whatever follow-up work the merged PR${n.length===1?"":"s"} unblock${n.length===1?"s":""} \u2014 and note the unmerged one${r.length===1?"":"s"}: that work did NOT land, so check whether it still needs to ship. If nothing needs doing, say so briefly.`:"This is an automated merge notification: pick up whatever follow-up work these merges unblock (dependent tasks, cleanup, next steps that were waiting on them). If nothing needs doing, say so briefly.":"This is an automated notification: NONE of the PRs landed \u2014 the work did not ship. Check whether it still needs to go out another way. If not, say so briefly.";return`${o}
|
|
237
241
|
${s}
|
|
238
|
-
${a}`}async function Ch(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Ba()?!1:In(e.name)}async function Ui(e,t,n){return!t||e.archivedAt||Mr.has(t.state)?!1:(await ee({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Ht(t.name).catch(()=>{}),t.state="stopped",Ji(e))}async function Ji(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=oh.get(r.name);if(o)return o;let s=Ej(e,r,{freshSession:t,reason:n}).finally(()=>oh.delete(r.name));return oh.set(r.name,s),s}async function Ej(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Mr.has(t.state)||await fe(t.name))return!1;let o=n===!0,s=o?To():null,a=o?Pa(ph(t.agent),s,t.name):mh(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Xc(a);if(i===Yo)return ve(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===Xo&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(Gs(t)))}:null,l=Date.now();if(o){let u=Gs(t),d=u[u.length-1];d.endedAt==null&&(d.endedAt=l),d.endReason==null&&(d.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=await cr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=wa(u.hookToken),Ka({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&ve(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),ve(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),ve(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Mh(e,{freshSession:t=!1}={}){let n=await X(),r=le(n,e);if(!r)throw new Error(`track "${e}" not found`);if(!r.archivedAt)throw new Error(`track "${e}" is not archived`);return delete r.archivedAt,Fi.delete(e),ve(r,"unarchived",{}),await Ji(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await Q(),Dt(r)}async function Nh(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>uh)throw new Error(`name too long (max ${uh} chars)`);let r=await X(),o=le(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return Dt(o);let s=o.name;return o.name=n,ve(o,"renamed",{from:s,to:n}),await Q(),Dt(o)}async function Ph(){let e=await X(),t=0;for(let n of e.tracks||[])n.transferredTo&&Il(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await Q(),t}async function Dh(){let e=await X(),t=new Set((await Ut()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!qe(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(qe(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ht(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):ve(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Mr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Q(),r}async function $u(e,{freshSession:t=!1}={}){let n=await X(),r=le(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);Hh(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Mr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await fe(r.leadBox)){let i=await Oe(r.leadBox,{lines:ze}).catch(()=>"");if(ll(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ht(r.leadBox),o.state="stopped"}let a=await Ji(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await Q(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Dt(r)}async function Bh(e,t,n){let r=await oe(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await F(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await $u(e),await O(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function wT(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return ou({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function Gs(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function ku(e,t){let n=await X(),r=le(n,e);r&&(r.handoff={...r.handoff||{},...t},await Q())}function LT(e,t=Date.now()){let n=e?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=e.boxes?.find?.(i=>i.name===e.leadBox)||null;return(o?e.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&e.leadBox===n.toBox)?{...n,step:"done",finishedAt:t,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${r}"; lead "${e.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function Lh(){let e=await X(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=LT(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await fe(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=Gs(l),f=d[d.length-1],p=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:xo,at:u},ve(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await ee({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await ee({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ht(i).catch(()=>{}),a&&await Ht(a).catch(()=>{})}return t&&await Q(),t}async function Ru(e,t,n=!0,r=null){let o=Vs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=le(await X(),e);if(!s)throw new Error(`track "${e}" not found`);Vi(e,"handoff");let a=await qi(t,n),i=uo(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await fe(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:ST(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Vs.set(e,l),await ku(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),_j(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await ku(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ee({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ee({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function _j(e,t,n,r,o){let s=async(D,S={})=>{e.step=D,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${D}`),await ku(e.trackId,{step:D,toBox:e.toBox}),await ee({flow:"lead-handoff",phase:D,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:S.sessionName,...S})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await X(),i=le(a,e.trackId)?.boxes.find(D=>D.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",uo(c))e.checkpoint="skipped-capped";else try{await Gf(e.fromBox,{timeoutMs:xT})}catch(D){e.checkpoint="reconstructed",await ee({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:D?.message||String(D)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Vf(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await ep(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=kb(f.launchCmd);await ur({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Js(o),launchEcho:p}).catch(D=>{if(D?.code!=="cli-not-running")throw D});let m=_f(await Oe(e.fromBox,{lines:ze}).catch(()=>""),p);if(!is(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let D=mh(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await ep(e.fromBox,{agentCmd:D,agent:c,resumeSessionId:l,pinned:u});let S=await X(),A=le(S,e.trackId);if(A){let R=A.boxes.find(E=>E.name===e.fromBox);R&&(R.state="live"),ve(A,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await Q()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await X(),y=le(w,e.trackId);if(!y)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let x=y.boxes.find(D=>D.name===e.fromBox);if(!x)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let T=Date.now(),I=Gs(x),$=I[I.length-1];$.endedAt=T,e.reason&&($.endReason=e.reason),I.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:T,endedAt:null}),x.agent=o,x.sessionId=f.sessionId,x.pinned=f.pinned,x.state="live",x.role="lead",y.pendingHandoffHydrate={artifact:e.statusArtifact||xo,at:T},ve(y,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:I.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await Q(),await s("done",{sessionName:e.fromBox}),await ku(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Uh(e){let t=fh.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await X(),r=le(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=Vs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of At.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return fh.set(e,s),Ij(s,r).catch(a=>{s.error=a?.message||String(a),s.finishedAt=Date.now(),console.error(`[transfer-out ${e}] FAILED at step "${s.step}": ${s.error}`)}),s}async function Ij(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await fe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&uo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Gf(t.leadBox,{timeoutMs:xT})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Vf(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of Et(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Jt(u.dir,["push","origin",o],dh);let{stdout:f}=await Jt(u.dir,["rev-parse","HEAD"]);d.baseSha=f.trim(),d.pushed=!0}catch(f){d.error=String(f?.message||f).split(`
|
|
239
|
-
`)[0]}s.push(
|
|
240
|
-
`)[0]}else l?.sha&&(E="wip snapshots cover single-repo worktrees only");await dT({id:e,cwd:I}).catch(()=>({restored:!1}));let U=J.join(I,xo),se=null;(await qc(I)).restored?se="checkpoint":typeof u=="string"&&u.trim()&&(await K.mkdir(J.dirname(U),{recursive:!0}),await K.writeFile(U,u),se="brief");let C=new Set(x.tracks.flatMap(B=>(B.boxes||[]).map(ye=>ye.name))),Z=null;p&&!C.has(p)&&!await fe(p).catch(()=>null)?Z=p:(Z=`${t.trim()} lead`,C.has(Zc(Z))&&(Z=`${t.trim().slice(0,50).trim()} ${e} lead`));let Y=await cr(Z,I,{agentCmd:y,agent:n});Ka({cwd:Y.cwd,sessionName:Y.name,agent:n,agentCmd:y});let te=Date.now(),we=jh(Y,"lead","live"),ce=(Array.isArray(d)?d:[]).filter(B=>B&&typeof B=="object").map(B=>({...B})),V=ce[ce.length-1];V&&V.endedAt==null&&(V.endedAt=te,V.endReason==null&&(V.endReason=o==="recover"?"node-offline":"node-transfer")),ce.push({agent:n,sessionId:Y.sessionId??null,pinned:Y.pinned===!0,startedAt:te,endedAt:null}),we.epochs=ce;let ge={id:e,name:t.trim(),cwd:Y.cwd,workspace:$,leadBox:Y.name,boxes:[we],events:[],createdAt:Number.isFinite(f)?f:te,...se?{pendingHandoffHydrate:{artifact:xo,at:te}}:{}};return ve(ge,"transferred-in",{box:Y.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,...o==="recover"?{downSince:Number.isFinite(w)?w:null}:{},branch:$.branch??null,wip:R,...L?{wipAdvanced:L}:{},epoch:ce.length-1,name:ge.name,cwd:ge.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:B,base:ye,baseRef:be,repo:_e})=>({name:B,base:ye,baseRef:be,repo:_e}))}:{}}}),x.tracks.push(ge),await Q(),{...await Dt(ge),adopt:{wipApplied:R,wipSkipped:E,...L?{wipAdvanced:L}:{},codeless:D,artifactSource:se}}}catch(A){for(let R of S)await vt(R.base,R.dir).catch(()=>{});throw await K.rm(I,{recursive:!0,force:!0}).catch(()=>{}),A}}async function FT(e,t,n){let r=await X(),o=le(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await Q())}async function Hs(e,t){e.step=t,await Yi(e)}function UT(e,t=Date.now()){if(!e||e.finishedAt)return null;let n=e.step;return["snapshot","spawn","working"].includes(n)?{...e,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...e,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${n}"; the lead's working tree may hold a PARTIAL merge \u2014 review git status. The subtask's work is preserved on branch ${e.branch}`,recovered:"partial-merge"}}function HT(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function Mj(e,t,n){if(!HT(n)||At.has(t)||!await fe(n.box).catch(()=>null))return!1;try{await K.access(n.dir)}catch{return!1}let o=await fe(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return At.set(t,s),qT(s,o).catch(a=>WT(s,a)),!0}async function Kh(){let e=await X(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(At.has(r))continue;if(await Mj(n,r,o)){t=!0,ve(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ee({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=UT(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Mr.has(i.state)&&(i.state="canceled"),ve(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await ee({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await yT(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Mr.has(r.state)||At.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?J.join(zs,`${n.id}-${r.subtaskId}`):null;await ee({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await yT(n,r.name,o)}}return t=await Nj(e)||t,t&&await Q(),t}async function yT(e,t,n){if(t&&await Ht(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Et(e.workspace)){let s=J.join(n,o.name);await vt(o.base,s).then(()=>!0,()=>!1)||await vt(o.dir,s).catch(()=>{})}await K.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await fe(e.leadBox).catch(()=>null);r?.cwd&&await vt(r.cwd,n).catch(()=>{})}async function Nj(e){let t;try{t=await K.readdir(zs)}catch{return!1}let n=new Set;for(let o of e.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of At.values())o.dir&&n.add(J.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=J.join(zs,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Et(a.workspace))await vt(i.base,J.join(s,i.name)).catch(()=>{});await K.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await fe(a.leadBox).catch(()=>null):null;i?.cwd?await vt(i.cwd,s).catch(()=>{}):await K.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ee({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Vh(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!ms(n))throw new Error("task (string) required");let s=await qi(t,r),a=uo(t);if(a){let p=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=le(await X(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await fe(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=Et(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await hs(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await hs(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=To().slice(0,8),f={id:d,trackId:e,trackName:i.name,agent:t,task:ms(n),skipPermissions:r,reason:ST(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Gh(i.name)}-${d}`,base:null,repos:u,dir:J.join(zs,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return At.set(d,f),await Yi(f),Pj(f,c,s).catch(p=>WT(f,p)),f}async function WT(e,t){e.failedStep=e.step,e.error=t instanceof Hi?"canceled":t?.message||String(t),e.step=t instanceof Hi?"canceled":"error",e.finishedAt=Date.now(),await Jh(e).catch(()=>{}),await Yi(e).catch(()=>{}),await Lj(e).catch(()=>{})}function Mu(e){let t=At.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");return t.canceled=!0,t}function zh(e){let t=At.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");if(t.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${t.step})`);return t.forceMerge=!0,t}async function Pj(e,t,n){if(await Hs(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await ws(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Yi(e)}else e.base=await ws(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Hs(e,"spawn"),await K.mkdir(zs,{recursive:!0}),e.repos){await K.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await gs(a.leadDir,J.join(e.dir,a.name),e.branch,a.base);let s=Et((le(await X(),e.trackId)||{}).workspace);await zi(e.dir,e.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),e.branch)}else await gs(t.cwd,e.dir,e.branch,e.base),await Li(e.dir);await Dj(e,t);let r=e.trackName.slice(0,38).trim(),o=await cr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await Bj(e,o),await TT(e.trackId,o.name,DB(e.task,!!e.repos),{kind:"subtask"}),await qT(e,t)}async function qT(e,t){await Hs(e,"working");let n=J.join(e.dir,bu);if(await Uj(n,e),e.canceled)throw new Hi;if(await Hs(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=J.join(e.dir,l.name);await vp(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:wT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Ap(l.leadDir,l.base,e.branch,Us),l.files=await Ep(l.leadDir,l.base,e.branch,Us);let d=await _p(l.leadDir,l.base,e.branch,Us);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(f=>J.join(l.name,f))),i.push(...l.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${l.name}: ${f}`:J.join(l.name,f))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await vp(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:wT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Ap(t.cwd,e.base,e.branch,Us),e.files=await Ep(t.cwd,e.base,e.branch,Us);let s=await _p(t.cwd,e.base,e.branch,Us);e.applied=s.applied,e.conflicts=s.conflicts}await Yi(e);let r=await Fj(e,t,n);await Hs(e,"resume"),await TT(e.trackId,e.leadBox,BB(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Hs(e,"reap"),await Jh(e),e.step="done";let o=Date.now();await FT(e.trackId,e.id,{...Cu(e),finishedAt:o}),await jj(e,o),e.finishedAt=o}async function Dj(e,t){let n=J.join(e.dir,J.dirname(Pi));await K.mkdir(n,{recursive:!0});let r="";try{r=await rb(t.cwd,t.sessionId)}catch{}if(!r)try{r=await K.readFile(J.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
242
|
+
${a}`}async function zh(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Wa()?!1:Mn(e.name)}async function Yi(e,t,n){return!t||e.archivedAt||Fr.has(t.state)?!1:(await re({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Yt(t.name).catch(()=>{}),t.state="stopped",rc(e))}async function rc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=bh.get(r.name);if(o)return o;let s=gL(e,r,{freshSession:t,reason:n}).finally(()=>bh.delete(r.name));return bh.set(r.name,s),s}async function gL(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Fr.has(t.state)||await ge(t.name))return!1;let o=n===!0,s=o?Po():null,a=o?Ua(Ih(t.agent),s,t.name):$h(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await sl(a);if(i===as)return Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===is&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(Qs(t)))}:null,l=Date.now();if(o){let u=Qs(t),f=u[u.length-1];f.endedAt==null&&(f.endedAt=l),f.endReason==null&&(f.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=await pr(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=va(u.hookToken),Xa({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),t.state="live",o&&Te(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session"}),Te(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Fu(e,{freshSession:t=!1}={}){let n=await Z(),r=he(n,e);if(!r)throw new Error(`track "${e}" not found`);if(!r.archivedAt)throw new Error(`track "${e}" is not archived`);return delete r.archivedAt,zi.delete(e),Te(r,"unarchived",{}),await rc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ee(),Mt(r)}async function Yh(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Eh)throw new Error(`name too long (max ${Eh} chars)`);let r=await Z(),o=he(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return Mt(o);let s=o.name;return o.name=n,Te(o,"renamed",{from:s,to:n}),await ee(),Mt(o)}async function Jh(){let e=await Z(),t=0;for(let n of e.tracks||[])n.transferredTo&&Dl(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ee(),t}async function Xh(){let e=await Z(),t=new Set((await zt()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ve(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ve(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Yt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Te(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Fr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ee(),r}async function Uu(e,{freshSession:t=!1}={}){let n=await Z(),r=he(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);rw(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Fr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ge(r.leadBox)){let i=await Ce(r.leadBox,{lines:Xe}).catch(()=>"");if(gl(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Yt(r.leadBox),o.state="stopped"}let a=await rc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ee(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Mt(r)}async function Zh(e,t,n){let r=await te(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await F(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await Uu(e),await $(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function zT(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return hu({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function Qs(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Ou(e,t){let n=await Z(),r=he(n,e);r&&(r.handoff={...r.handoff||{},...t},await ee())}function wE(e,t=Date.now()){let n=e?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=e.boxes?.find?.(i=>i.name===e.leadBox)||null;return(o?e.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&e.leadBox===n.toBox)?{...n,step:"done",finishedAt:t,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${r}"; lead "${e.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function ew(){let e=await Z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=wE(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ge(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),f=Qs(l),d=f[f.length-1],p=d.agent,h=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Co,at:u},Te(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await re({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await re({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Yt(i).catch(()=>{}),a&&await Yt(a).catch(()=>{})}return t&&await ee(),t}async function Hu(e,t,n=!0,r=null){let o=ea.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=he(await Z(),e);if(!s)throw new Error(`track "${e}" not found`);tc(e,"handoff");let a=await Zi(t,n),i=yo(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await ge(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:tE(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ea.set(e,l),await Ou(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),yL(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Ou(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),re({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),re({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function yL(e,t,n,r,o){let s=async(B,T={})=>{e.step=B,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${B}`),await Ou(e.trackId,{step:B,toBox:e.toBox}),await re({flow:"lead-handoff",phase:B,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:T.sessionName,...T})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await Z(),i=he(a,e.trackId)?.boxes.find(B=>B.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",yo(c))e.checkpoint="skipped-capped";else try{await tp(e.fromBox,{timeoutMs:QT})}catch(B){e.checkpoint="reconstructed",await re({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:B?.message||String(B)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await np(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await lp(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=Db(d.launchCmd);await hr({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:na(o),launchEcho:p}).catch(B=>{if(B?.code!=="cli-not-running")throw B});let h=Bf(await Ce(e.fromBox,{lines:Xe}).catch(()=>""),p);if(!gs(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let B=$h(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await lp(e.fromBox,{agentCmd:B,agent:c,resumeSessionId:l,pinned:u});let T=await Z(),_=he(T,e.trackId);if(_){let O=_.boxes.find(I=>I.name===e.fromBox);O&&(O.state="live"),Te(_,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await ee()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await Z(),g=he(w,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let k=g.boxes.find(B=>B.name===e.fromBox);if(!k)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let S=Date.now(),E=Qs(k),R=E[E.length-1];R.endedAt=S,e.reason&&(R.endReason=e.reason),E.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:S,endedAt:null}),k.agent=o,k.sessionId=d.sessionId,k.pinned=d.pinned,k.state="live",k.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||Co,at:S},Te(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:E.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ee(),await s("done",{sessionName:e.fromBox}),await Ou(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function nw(e){let t=_h.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await Z(),r=he(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=ea.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Nt.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return _h.set(e,s),bL(s,r).catch(a=>{s.error=a?.message||String(a),s.finishedAt=Date.now(),console.error(`[transfer-out ${e}] FAILED at step "${s.step}": ${s.error}`)}),s}async function bL(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ge(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&yo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await tp(t.leadBox,{timeoutMs:QT})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await np(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of Dt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await nn(u.dir,["push","origin",o],Ah);let{stdout:d}=await nn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
243
|
+
`)[0]}s.push(f)}e.step="wip";let a=await lh({id:t.id,cwd:t.cwd},{force:!0}),i=await Z(),c=he(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Qs(l),await ee(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:f,baseSha:d})=>({name:u,repo:f,baseSha:d})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:f,error:d})=>({name:u,repo:f,error:d})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function rw(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 Wu(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await Z(),o=he(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};jh(e),await Kh(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Dl(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await uh({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,Te(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await ee(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function ow(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Wu(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 YT(e,t,n){try{await Nl(e,t,n)}catch{await nn(e,["worktree","prune"]).catch(()=>{}),await Nl(e,t,n)}return t}async function sw(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(kh.has(t))throw new Error("an adopt is already running for this Track");kh.set(t,{startedAt:Date.now()});try{return await kL({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{kh.delete(t)}}async function kL({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:f=[],createdAt:d=null,leadBox:p=null,reason:h=null,downSince:w=null}){let g=await Zi(n,r),k=await Z(),S=he(k,e);if(S&&!S.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);S&&(await uh({id:S.id,cwd:S.cwd}).catch(()=>{}),await pE(S),k.tracks.splice(k.tracks.indexOf(S),1),await ee()),await z.mkdir(Hi,{recursive:!0});let E=J.join(Hi,e);await z.rm(E,{recursive:!0,force:!0}).catch(()=>{});let R=null,B=!1,T=[];try{let _=[];if(i)for(let L of Array.isArray(c)?c:[]){let ce=typeof L=="string"?L:L?.repo;if(!ce||!/^[^/\s]+\/[^/\s]+$/.test(ce))continue;let ue=await Zo(ce)??await Fc(ce);await nn(ue,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Ah),_.push({slug:ce,base:ue,name:typeof L=="object"?L?.name??null:null})}if(i&&_.length===1){let{base:L,slug:ce}=_[0];await YT(L,E,i),T.push({base:L,dir:E}),await Vi(E);let ue=await Is(L);R={dir:E,base:L,branch:i,kind:"worktree",baseRef:ue,repo:ce}}else if(i&&_.length>=2){await z.mkdir(E,{recursive:!0});let L=new Set([".bostrat"]),ce=[];for(let ue of _){let Ae=ue.name||J.basename(ue.base);L.has(Ae)&&(Ae=ue.slug.replace("/","-"));for(let je=2;L.has(Ae);je++)Ae=`${J.basename(ue.base)}-${je}`;L.add(Ae);let _e=J.join(E,Ae);await YT(ue.base,_e,i),T.push({base:ue.base,dir:_e});let xt=await Is(ue.base);ce.push({name:Ae,dir:_e,base:ue.base,baseRef:xt,repo:ue.slug})}await nc(E,ce,i),R={dir:E,kind:"multi",branch:i,repos:ce}}else B=!0,await z.mkdir(E,{recursive:!0}),await Cp(E),await Vi(E),R={dir:E,base:null,branch:null,kind:"repo"};let O=!1,I=null,U=null;if(l?.sha&&R.kind==="worktree")try{await nn(E,["fetch","origin",l.ref||ch(e)],Ah);let{stdout:L}=await nn(E,["rev-parse","FETCH_HEAD^"]),{stdout:ce}=await nn(E,["rev-parse","HEAD"]),ue=L.trim()===ce.trim();ue||await nn(E,["merge-base","--is-ancestor",ce.trim(),L.trim()]).then(()=>!0,()=>!1)&&(await nn(E,["reset","--hard",L.trim()]),U={from:ce.trim(),to:L.trim()},ue=!0),ue?(await nn(E,["read-tree","--reset","-u","FETCH_HEAD"]),await nn(E,["reset","-q"]),O=!0):I="snapshot does not parent the fetched branch head"}catch(L){I=String(L?.message||L).split(`
|
|
244
|
+
`)[0]}else l?.sha&&(I="wip snapshots cover single-repo worktrees only");await WT({id:e,cwd:E}).catch(()=>({restored:!1}));let H=J.join(E,Co),ae=null;(await Zc(E)).restored?ae="checkpoint":typeof u=="string"&&u.trim()&&(await z.mkdir(J.dirname(H),{recursive:!0}),await z.writeFile(H,u),ae="brief");let C=new Set(k.tracks.flatMap(L=>(L.boxes||[]).map(ce=>ce.name))),Q=null;p&&!C.has(p)&&!await ge(p).catch(()=>null)?Q=p:(Q=`${t.trim()} lead`,C.has(al(Q))&&(Q=`${t.trim().slice(0,50).trim()} ${e} lead`));let ve=await pr(Q,E,{agentCmd:g,agent:n});Xa({cwd:ve.cwd,sessionName:ve.name,agent:n,agentCmd:g});let W=Date.now(),be=Qh(ve,"lead","live"),se=(Array.isArray(f)?f:[]).filter(L=>L&&typeof L=="object").map(L=>({...L})),pe=se[se.length-1];pe&&pe.endedAt==null&&(pe.endedAt=W,pe.endReason==null&&(pe.endReason=o==="recover"?"node-offline":"node-transfer")),se.push({agent:n,sessionId:ve.sessionId??null,pinned:ve.pinned===!0,startedAt:W,endedAt:null}),be.epochs=se;let ie={id:e,name:t.trim(),cwd:ve.cwd,workspace:R,leadBox:ve.name,boxes:[be],events:[],createdAt:Number.isFinite(d)?d:W,...ae?{pendingHandoffHydrate:{artifact:Co,at:W}}:{}};return Te(ie,"transferred-in",{box:ve.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(w)?w:null}:{},branch:R.branch??null,wip:O,...U?{wipAdvanced:U}:{},epoch:se.length-1,name:ie.name,cwd:ie.cwd,workspace:{base:R.base??null,branch:R.branch??null,kind:R.kind,...R.baseRef?{baseRef:R.baseRef}:{},...R.repo?{repo:R.repo}:{},...R.repos?{repos:R.repos.map(({name:L,base:ce,baseRef:ue,repo:Ae})=>({name:L,base:ce,baseRef:ue,repo:Ae}))}:{}}}),k.tracks.push(ie),await ee(),{...await Mt(ie),adopt:{wipApplied:O,wipSkipped:I,...U?{wipAdvanced:U}:{},codeless:B,artifactSource:ae}}}catch(_){for(let O of T)await Rt(O.base,O.dir).catch(()=>{});throw await z.rm(E,{recursive:!0,force:!0}).catch(()=>{}),_}}async function gE(e,t,n){let r=await Z(),o=he(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ee())}async function Ys(e,t){e.step=t,await oc(e)}function yE(e,t=Date.now()){if(!e||e.finishedAt)return null;let n=e.step;return["snapshot","spawn","working"].includes(n)?{...e,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...e,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${n}"; the lead's working tree may hold a PARTIAL merge \u2014 review git status. The subtask's work is preserved on branch ${e.branch}`,recovered:"partial-merge"}}function bE(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function TL(e,t,n){if(!bE(n)||Nt.has(t)||!await ge(n.box).catch(()=>null))return!1;try{await z.access(n.dir)}catch{return!1}let o=await ge(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Nt.set(t,s),xE(s,o).catch(a=>kE(s,a)),!0}async function aw(){let e=await Z(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Nt.has(r))continue;if(await TL(n,r,o)){t=!0,Te(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await re({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=yE(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Fr.has(i.state)&&(i.state="canceled"),Te(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await re({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await JT(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Fr.has(r.state)||Nt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?J.join(ta,`${n.id}-${r.subtaskId}`):null;await re({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await JT(n,r.name,o)}}return t=await EL(e)||t,t&&await ee(),t}async function JT(e,t,n){if(t&&await Yt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Dt(e.workspace)){let s=J.join(n,o.name);await Rt(o.base,s).then(()=>!0,()=>!1)||await Rt(o.dir,s).catch(()=>{})}await z.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ge(e.leadBox).catch(()=>null);r?.cwd&&await Rt(r.cwd,n).catch(()=>{})}async function EL(e){let t;try{t=await z.readdir(ta)}catch{return!1}let n=new Set;for(let o of e.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of Nt.values())o.dir&&n.add(J.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=J.join(ta,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Dt(a.workspace))await Rt(i.base,J.join(s,i.name)).catch(()=>{});await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ge(a.leadBox).catch(()=>null):null;i?.cwd?await Rt(i.cwd,s).catch(()=>{}):await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await re({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function cw(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Ts(n))throw new Error("task (string) required");let s=await Zi(t,r),a=yo(t);if(a){let p=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=he(await Z(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ge(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=Dt(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let h of p)if(!h?.dir||!await Es(h.dir))throw new Error(`tagged checkout "${h?.name??"?"}" is missing from the lead workspace (${h?.dir??"no dir"})`);u=p.map(h=>({name:h.name,leadDir:h.dir,base:null}))}else if(!await Es(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Po().slice(0,8),d={id:f,trackId:e,trackName:i.name,agent:t,task:Ts(n),skipPermissions:r,reason:tE(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${iw(i.name)}-${f}`,base:null,repos:u,dir:J.join(ta,`${e}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Nt.set(f,d),await oc(d),AL(d,c,s).catch(p=>kE(d,p)),d}async function kE(e,t){e.failedStep=e.step,e.error=t instanceof Ji?"canceled":t?.message||String(t),e.step=t instanceof Ji?"canceled":"error",e.finishedAt=Date.now(),await uw(e).catch(()=>{}),await oc(e).catch(()=>{}),await RL(e).catch(()=>{})}function Ku(e){let t=Nt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");return t.canceled=!0,t}function lw(e){let t=Nt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");if(t.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${t.step})`);return t.forceMerge=!0,t}async function AL(e,t,n){if(await Ys(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await As(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await oc(e)}else e.base=await As(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Ys(e,"spawn"),await z.mkdir(ta,{recursive:!0}),e.repos){await z.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await _s(a.leadDir,J.join(e.dir,a.name),e.branch,a.base);let s=Dt((he(await Z(),e.trackId)||{}).workspace);await nc(e.dir,e.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),e.branch)}else await _s(t.cwd,e.dir,e.branch,e.base),await Vi(e.dir);await _L(e,t);let r=e.trackName.slice(0,38).trim(),o=await pr(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await IL(e,o),await nE(e.trackId,o.name,_j(e.task,!!e.repos),{kind:"subtask"}),await xE(e,t)}async function xE(e,t){await Ys(e,"working");let n=J.join(e.dir,Ru);if(await CL(n,e),e.canceled)throw new Ji;if(await Ys(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=J.join(e.dir,l.name);await Op(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:zT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Np(l.leadDir,l.base,e.branch,zs),l.files=await Pp(l.leadDir,l.base,e.branch,zs);let f=await Dp(l.leadDir,l.base,e.branch,zs);l.applied=f.applied,l.conflicts=f.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(d=>J.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:J.join(l.name,d))),f.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Op(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:zT({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Np(t.cwd,e.base,e.branch,zs),e.files=await Pp(t.cwd,e.base,e.branch,zs);let s=await Dp(t.cwd,e.base,e.branch,zs);e.applied=s.applied,e.conflicts=s.conflicts}await oc(e);let r=await OL(e,t,n);await Ys(e,"resume"),await nE(e.trackId,e.leadBox,Ij(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Ys(e,"reap"),await uw(e),e.step="done";let o=Date.now();await gE(e.trackId,e.id,{...qu(e),finishedAt:o}),await $L(e,o),e.finishedAt=o}async function _L(e,t){let n=J.join(e.dir,J.dirname(Wi));await z.mkdir(n,{recursive:!0});let r="";try{r=await bb(t.cwd,t.sessionId)}catch{}if(!r)try{r=await z.readFile(J.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
241
245
|
|
|
242
246
|
**Delegated task:** ${e.task}
|
|
243
247
|
|
|
@@ -248,15 +252,15 @@ ${a}`}async function Ch(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
248
252
|
## Conversation so far (lead \u2192 you)
|
|
249
253
|
|
|
250
254
|
${r}
|
|
251
|
-
`;await
|
|
255
|
+
`;await z.writeFile(J.join(e.dir,Wi),o)}async function IL(e,t){let n=await Z(),r=he(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:va(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Te(r,"subtask-delegated",{to:t.name,toAgent:e.agent,task:e.task,subtaskId:e.id,skipPermissions:e.skipPermissions,reason:e.reason}),r.subtasks=r.subtasks||{},r.subtasks[e.id]={...r.subtasks[e.id]||{},...qu(e)},await ee())}async function $L(e,t=e.finishedAt){let n=await Z(),r=he(n,e.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=e.conflicts.length?"conflict":"merged",o.stat=e.stat),Te(r,"subtask-merged",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,stat:e.stat,conflicts:e.conflicts.length,files:e.files,durationMs:t?t-e.startedAt:null}),await ee()}async function RL(e){let t=await Z(),n=he(t,e.trackId);if(!n)return;let r=e.canceled||e.error==="canceled",o=n.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=r?"canceled":"failed"),Te(n,r?"subtask-canceled":"subtask-failed",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,failedStep:e.failedStep||e.step,error:e.error,durationMs:e.finishedAt?e.finishedAt-e.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[e.id]={...n.subtasks[e.id]||{},...qu(e)},await ee()}async function OL(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=J.join(t.cwd,r);await z.mkdir(J.dirname(o),{recursive:!0}),await z.copyFile(n,o)}catch{}return r}async function uw(e){if(e.dir&&await Zd(e.dir).catch(()=>{}),e.box&&await Yt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await Rt(t.leadDir,J.join(e.dir,t.name)).catch(()=>{});await z.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await ge(e.leadBox).catch(()=>null))?.cwd;n&&await Rt(n,e.dir).catch(()=>{})}}async function CL(e,t){let n=(t.startedAt||Date.now())+xL;for(;;){if(t.canceled||t.forceMerge)return;try{await z.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await No(vL)}}function No(e){return new Promise(t=>setTimeout(t,e))}var XT,En,ZT,Tj,Hi,Ej,Cu,Xi,Lr,Co,QT,Aj,Ru,Wi,_j,Ij,qt,ph,he,mh,Te,hh,wh,Oj,Fr,KT,Nj,Pj,Dj,Bj,Fj,qi,GT,Wj,qj,Kj,Gj,Vj,Js,Ki,Xj,Qj,eL,rL,uE,Gi,dE,iL,Ui,gh,cL,yh,fL,zi,pL,mL,VT,bh,Eh,Qh,ea,ra,Ah,nn,_h,tw,kh,xL,vL,ta,zs,Nt,SL,jo,qu,oc,iw,Ji,Nr=j(()=>{Oa();lt();op();zk();Za();cr();pl();Qk();it();dn();Yo();fl();w0();io();Ap();Ip();M0();bl();Uf();ml();Xf();Xt();Pl();ar();Bn();Qa();Fl();ax();Wp();eS();Qf();Vf();nS();Em();me();wu();xu();Si();bo();qm();Ks();eh();Iu();ih();Fn();uu();jT();dh();qT();Qc();XT=Sj(vj),En=process.env.BOSTRAT_STATE_DIR||J.join(process.env.HOME||"/tmp",".bostrat"),ZT=J.join(En,"tracks.json"),Tj=J.join(En,"events.jsonl"),Hi=J.join(En,"workspaces"),Ej=process.env.BOSTRAT_DEFAULT_REPO||null,Cu={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"}},Xi=Object.keys(Cu);Lr={at:0,value:null};Co=".bostrat/handoff/latest.md",QT=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;Aj=/\(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\.\)$/;Ru=".bostrat/handoff/subtask-result.md",Wi=".bostrat/handoff/lead-context.md",_j=(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 ${Wi} \u2014 read that first for context. Your scoped task:
|
|
252
256
|
|
|
253
257
|
${e}
|
|
254
258
|
|
|
255
|
-
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${
|
|
259
|
+
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${Ru} (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 ${Wi} \u2014 read that first for context. Your scoped task:
|
|
256
260
|
|
|
257
261
|
${e}
|
|
258
262
|
|
|
259
|
-
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${
|
|
263
|
+
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${Ru} 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.`,Ij=(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."),qt=null,ph=null;he=(e,t)=>e.tracks.find(n=>n.id===t)||null,mh=Promise.resolve();Te=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),$j(e.id,r),lx(e,r),r};Sb(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await Z()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(Te(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await ee().catch(()=>{}))});hh=J.join(En,"plane-link","update-notice.json");wh=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),Oj=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Fr=new Set(["merged","conflict","canceled","failed","handed-off"]),KT=new Map;Nj=5e3;Pj={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};Dj=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];Bj=10*6e4;Fj=1500,qi=new Map;GT="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.";Wj=4e3,qj=3,Kj=[300,350,400],Gj=1e3,Vj=12,Js=new Map,Ki=new Map;Xj=[250,400,500,600];Qj=3e4,eL=[350,650,900];rL=12e4;uE=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}))});Gi="# bostrat workspace",dE=`${Gi}
|
|
260
264
|
|
|
261
265
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
262
266
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -534,56 +538,91 @@ toward the chat, never the only record.
|
|
|
534
538
|
|
|
535
539
|
When you FINISH a piece of implementation work (feature built, bug fixed, checks
|
|
536
540
|
green) and the operator has not already told you to ship it: do NOT commit, push,
|
|
537
|
-
or open a PR on your own. Instead
|
|
538
|
-
|
|
539
|
-
|
|
541
|
+
or open a PR on your own. Instead OFFER to, as a structured plan \u2014 write the ship
|
|
542
|
+
you would run as JSON and run:
|
|
543
|
+
|
|
544
|
+
bostrat-ship offer <plan.json>
|
|
545
|
+
|
|
546
|
+
Plan shape: { "label": "Ship", "why": "<one line on what lands>", "ship": {
|
|
547
|
+
"actions": ["commit","push","pr"], "toPr": 641, "rebase": "offered"|"none",
|
|
548
|
+
"base": "main", "repos": ["<checkout>"] } } \u2014 actions is what you would do
|
|
549
|
+
(drop "pr" when the branch's PR is already open and name it in toPr; set
|
|
550
|
+
rebase: "offered" when the branch is behind its base); base and repos are
|
|
551
|
+
optional. Bostrat grounds the plan in git and renders one-tap ship controls in the
|
|
540
552
|
operator's chat; the go-ahead comes back as a normal message telling you exactly
|
|
541
|
-
which steps to run (and which to skip).
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
553
|
+
which steps to run (and which to skip). Run it as the LAST thing in the turn and
|
|
554
|
+
then stop \u2014 it IS your closing line. If you cannot run it, END your final message
|
|
555
|
+
with a short, explicit offer \u2014 e.g. "Want me to commit and push this, or open a
|
|
556
|
+
PR?" \u2014 as its own last line; Bostrat still detects that sentence. An offer buried
|
|
557
|
+
mid-message, phrased as a statement, or omitted entirely renders no controls \u2014 so
|
|
558
|
+
close EVERY completed implementation turn one of these two ways, every time. When
|
|
559
|
+
the operator has already asked you to commit / push / open the PR, just do it; do
|
|
560
|
+
not re-offer.
|
|
561
|
+
|
|
562
|
+
FOLLOW-UPS: when the work you just finished has a natural next step that only
|
|
563
|
+
makes sense AFTER a lifecycle moment \u2014 deploying after the merge, publishing a
|
|
564
|
+
package after the release PR lands, re-running a migration after CI goes green \u2014
|
|
565
|
+
arm it BEFORE your closing line:
|
|
566
|
+
|
|
567
|
+
bostrat-followup offer <plan.json>
|
|
568
|
+
|
|
569
|
+
Plan: { "label": "Deploy to Pi", "trigger": "pr.merged"|"ship.done"|"pr.opened"|
|
|
570
|
+
"checks.green"|"now", "prompt": "<the full instruction a fresh session could
|
|
571
|
+
execute \u2014 the Track may be revived to run it>", "why": "<one line>" }. Bostrat
|
|
572
|
+
offers it as a one-tap card at that moment; now renders the card at the end of
|
|
573
|
+
this turn (a structured "shall I do X next?" instead of a question \u2014 then stop).
|
|
574
|
+
Arm freely; it is cheap, Track-local, and the operator answers later. Don't arm
|
|
575
|
+
the generic "pick up whatever this unblocks" \u2014 that nudge already exists.
|
|
576
|
+
bostrat-followup propose <plan.json> saves a STANDING follow-up (offered on
|
|
577
|
+
every matching moment, listed in Settings) \u2014 propose one ONLY when the operator
|
|
578
|
+
asks ("always deploy after merging", "remember this") or you have seen them ask
|
|
579
|
+
for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
580
|
+
(a deleted one must never be re-proposed), keep the prompt free of this Track's
|
|
581
|
+
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
582
|
+
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
583
|
+
follow-up on its own; the operator's tap is the gate.
|
|
584
|
+
`,iL=dE,Ui=dE+ik;gh=e=>typeof e=="string"&&(e===Gi||e.startsWith(`${Gi}
|
|
585
|
+
`)),cL="# bostrat multi-repo workspace";yh=Ui.replace(`${Gi}
|
|
586
|
+
`,`${Gi}
|
|
548
587
|
|
|
549
588
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
550
589
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
551
590
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
552
591
|
When working anywhere else, ignore everything below.
|
|
553
|
-
`);xj=new Set(["claude","codex","cursor"]);Fi=new Set,vj=e=>Fi.has(e);Sj=9e4;hT={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};oh=new Map;uh=120;jh=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:wa(e.hookToken),createdAt:Date.now()});Vs=new Map,Ys=e=>Vs.get(e)||(Pt?le(Pt,e)?.handoff:null)||null;dh=6e4,Jt=(e,t,n=15e3)=>bT("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),fh=new Map,Fh=e=>fh.get(e)||null;sh=new Map;Rj=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,Oj=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,zs=J.join(yn,"worktrees"),Us=[Pi,bu],At=new Map,Cj=e=>At.get(e)||(Pt?Pt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Eo=e=>{let t=new Map([...At.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Pt?le(Pt,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},Cu=e=>({id:e.id,trackId:e.trackId,trackName:e.trackName,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,reason:e.reason||null,step:e.step,failedStep:e.failedStep||null,box:e.box,branch:e.branch,base:e.base,repos:e.repos||null,dir:e.dir,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt});Yi=e=>FT(e.trackId,e.id,Cu(e));Gh=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Hi=class extends Error{}});nt();import ke from"node:path";import at from"node:fs";import aH from"node:readline";import{fileURLToPath as iH}from"node:url";import Lt from"express";import cH from"node:http";import lH from"node:https";import hd from"node:os";import{WebSocketServer as uH}from"ws";nt();import LR from"node-pty";async function Py(e,t,n={}){if(!Ne(t)){nl(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await ir()){nl(e,`host tmux server not running on ${xt}`),e.close(1011,"tmux server unavailable");return}if(!await fe(t)){nl(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Ny(n.cols,120,Dy),o=Ny(n.rows,32,By),s;try{s=LR.spawn(W,["-S",xt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){nl(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&rf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(rf(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&WR(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),e.on("close",()=>{try{s.kill()}catch{}HR(t)})}var FR=6,UR=250;async function HR(e,t=FR,n=UR){for(let r=0;r<t;r++){if(await Xr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Dy=20,By=6;function WR(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Dy&&Number(t)>=By}function Ny(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function rf(e,t){try{e.send(JSON.stringify(t))}catch{}}function nl(e,t){rf(e,{type:"error",message:t})}function jy(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}qt();ol();Jf();_r();import{createRequire as dH}from"node:module";var Hj=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Yh({tracks:e=[],views:t=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of e)!o||o.archivedAt&&!o.archiving||o.transferredTo||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(Hj)&&r.push(`merge-back:${o.id}`));return t.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}import rA from"node:fs";import Pr from"node:fs/promises";import sL from"node:os";import tt from"node:path";import{spawn as aL}from"node:child_process";import{createRequire as iL}from"node:module";ue();Cl();import eA from"node:fs";import Zi from"node:fs/promises";import Kj from"node:http";import Xt from"node:path";import Pu from"node:os";import{createRequire as Gj}from"node:module";import Zh from"ws";import Wj from"node:path";import qj from"node:os";var Xi=process.env.BOSTRAT_STATE_DIR||Wj.join(process.env.HOME||qj.tmpdir(),".bostrat");function Xh(e=process.env){let t=(e.BOSTRAT_HOST||"").trim();if(t!=="all")return t||(e.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function GT(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Xh(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function VT(e){let t=String(e||"").trim();if(!t)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`wss://${t}`,r;try{r=new URL(n)}catch{return t}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):t}var Yt=(()=>{try{return Gj(import.meta.url)("../package.json").version||null}catch{return null}})(),zT=process.env.BOSTRAT_STATE_DIR||Xt.join(process.env.HOME||Pu.tmpdir(),".bostrat");function _o({env:e=process.env,pkgDir:t=Xt.resolve(Xt.dirname(new URL(import.meta.url).pathname),".."),exists:n=eA.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(Xt.join(t,"..",".git"))?"git-checkout":"dev"}function Qi(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function Nu(e){switch(e){case"npm-global":return"npm install -g bostrat-node && restart the bostrat-node service";case"git-checkout":return"scripts/deploy-pi.sh (or The Bakery \u2192 Deploy to Pi \u2192 bostrat)";case"container":return"Links \u2192 Fly \u2192 Update (or the plane's auto-update while the node sleeps)";default:return"update this node's bostrat-node checkout/install"}}function Vj({home:e=process.env.HOME||Pu.homedir(),env:t=process.env}={}){let n=r=>{try{return eA.statSync(r),!0}catch{return!1}};return{claude:n(Xt.join(e,".claude",".credentials.json")),codex:n(Xt.join(t.CODEX_HOME||Xt.join(e,".codex"),"auth.json"))}}var et=1,JT=1e3,YT=3e4,zj=6e4,Jj=1500,Yj=500,Nr=100,Xj=2e3,Zj=25e3,Qj=6e4,tA=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},eL=tA(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),tL=3e4,XT=10*6e4,nL=400,ZT=250,rL=tA(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Qh=4,oL=6e4,QT=6e4;function nA({url:e,token:t,nodeId:n=Pu.hostname(),name:r=Pu.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=oL,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:d=null,updatePolicy:f=null,installKind:p=_o(),selfUrl:m=`http://127.0.0.1:${process.env.PORT||3362}`,onDegraded:w=null,onRecovered:y=null,ingestMode:x=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:T=eL,unackedRetryMs:I=Qj,pingMs:$=Zj,maxInflightEvents:D=nL,httpTimeoutMs:S=rL,stallBackoffMinMs:A=tL,fetchImpl:R=globalThis.fetch,log:E=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=VT(e);let L=m.replace(/^http/,"ws"),U=new Set,se=new Map,G=new Map,C=Xt.join(zT,"plane-link","cursor.json"),Z=Xt.join(zT,"plane-link","dead-letter.ndjson"),Y=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),te={},we={},ce=new Map,V=null,ge=null,B=0,ye=0,be=0,_e=Promise.resolve(),Ie="ws",b=0,h=!1,g=0,_=!1,ne=Nr,ae=new Set,Te=[],Ee=null,Le=null,bt=!1,bd=!1,da=JT,fa=null,Do=null,Bo=null,vn=null,Sn=null,kd=!1,Ac=0;async function wI(){try{te=JSON.parse(await Zi.readFile(C,"utf8"))}catch{te={}}}function gI(){kd=!0,!Bo&&(Bo=setTimeout(async()=>{if(Bo=null,!!kd){kd=!1;try{await Zi.mkdir(Xt.dirname(C),{recursive:!0}),await Zi.writeFile(C,JSON.stringify(te))}catch(N){E.error("[plane-link] cursor flush failed:",N?.message||N)}}},Xj),Bo.unref?.())}function Ze(N){return!Le||Le.readyState!==Zh.OPEN?!1:(Le.send(JSON.stringify(N)),!0)}function pa(N){let P=Date.now();for(let{trackId:k,event:M}of N){let H=ce.get(M.id);ce.set(M.id,{trackId:k,seq:M.seq,event:M,sentAt:P,firstSentAt:H?.firstSentAt??P,retryAt:P+I,tries:(H?.tries??0)+1,nacks:H?.nacks??0})}return Ie==="http"?(xI(N),!0):Ze({v:et,ch:"events",type:"append",events:N})}function yI(N){let P=null;for(let k of ce.values())k.trackId===N&&(P==null||k.seq<P)&&(P=k.seq);return P}function xd(N){let P=we[N];if(P==null)return;let k=yI(N),M=k==null?P:Math.min(P,k-1);M>(te[N]??0)&&(te[N]=M,gI())}function ma(){if(V=Date.now(),ge!=null){let N=Date.now()-ge;E.log(`[plane-link] spine sync recovered after ${Math.round(N/1e3)}s degraded (${B} stall redial(s))`);let P=ge;ge=null,B=0,ye=0,h=!1,g=0;try{y?.({since:P,downMs:N})}catch(k){E.error(`[plane-link] onRecovered failed: ${k?.message||k}`)}}}function vd(N){if(!Array.isArray(N)||!N.length)return;let P=new Set;for(let k of N){let M=ce.get(k);M&&(ce.delete(k),(we[M.trackId]??0)<M.seq&&(we[M.trackId]=M.seq),P.add(M.trackId))}for(let k of P)xd(k);ma()}function lg(N,P){be++;let k={at:Date.now(),trackId:N.trackId,seq:N.seq,id:N.event?.id??null,type:N.event?.type??null,reason:P,event:N.event??null};E.error(`[plane-link] DEAD-LETTER ${N.trackId} seq ${N.seq} (${k.type??"?"}): plane rejected it permanently (${P}) \u2014 kept in ${Z}`),_e=_e.then(async()=>{await Zi.mkdir(Xt.dirname(Z),{recursive:!0}),await Zi.appendFile(Z,JSON.stringify(k)+`
|
|
554
|
-
`)}).catch(M=>E.error(`[plane-link] dead-letter write failed: ${M?.message||M}`))}function ug(N,P,k,M=null){if(!Array.isArray(N)||!N.length)return;let H=new Set,pe=Date.now();for(let Se of N){let Pe=ce.get(Se);Pe&&(k?(ce.delete(Se),lg(Pe,P),(we[Pe.trackId]??0)<Pe.seq&&(we[Pe.trackId]=Pe.seq),H.add(Pe.trackId)):(Pe.nacks++,Pe.retryAt=pe+Math.max(1e3,Number(M)||I)))}for(let Se of H)xd(Se);ma()}function dg(N=Date.now()){let P=1/0;for(let k of ce.values())k.firstSentAt<P&&(P=k.firstSentAt);return P===1/0?0:N-P}function fg(){if(!bt||!ce.size)return;let N=Date.now(),P=dg(N);if(P<T||V!=null&&N-V<T||Ie==="http"&&h||ye>N)return;B++;let k=Math.min(A*2**(B-1),XT);if(ge==null){ge=N-P,E.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(P/1e3)}s with ${ce.size} event(s) in flight (${Ie} door) \u2014 the plane is not answering appends`);try{w?.({since:ge,unacked:ce.size,oldestAgeMs:P,mode:Ie})}catch(M){E.error(`[plane-link] onDegraded failed: ${M?.message||M}`)}}if(Ie==="http"){h=!0,g=N+k,E.error(`[plane-link] ingest circuit OPEN (stall #${B}); probing /v1/ingest/health in ${Math.round(k/1e3)}s`);return}ye=N+k,E.error(`[plane-link] closing the link and redialing in ${Math.round(k/1e3)}s (stall redial #${B})`);try{Le?.terminate()}catch{}}function Sd(){if(!bt)return;let N=Date.now();if(Ie==="http"&&h)return;let P=[...ce.values()].filter(M=>M.event&&M.retryAt<=N&&!(Ie==="http"&&ae.has(M.trackId)));if(!P.length)return;P.sort((M,H)=>M.trackId===H.trackId?M.seq-H.seq:M.trackId<H.trackId?-1:1),E.log(`[plane-link] re-sending ${P.length} unacked event(s) the plane never confirmed`);let k=Ie==="http"?ne:Nr;if(Ie==="http"){let M=new Map;for(let H of P)(M.get(H.trackId)??M.set(H.trackId,[]).get(H.trackId)).push(H);for(let[,H]of M){if(b>=Qh)return;pa(H.slice(0,k).map(pe=>({trackId:pe.trackId,event:pe.event})))}return}for(let M=0;M<P.length;M+=k){let H=P.slice(M,M+k).map(pe=>({trackId:pe.trackId,event:pe.event}));if(!pa(H))return}}async function bI(N=null){for(;;){if(!bt)return!1;let P=ce.size<D,k=Ie!=="http"||b<Qh&&!h&&(!N||!ae.has(N));if(P&&k)return!0;await new Promise(M=>setTimeout(M,100))}}function Td(){if(Ee=null,!bt||!Te.length)return;let N=Te;if(Te=[],Ie!=="http"){for(let k=0;k<N.length;k+=Nr)pa(N.slice(k,k+Nr));return}let P=new Map;for(let k of N)(P.get(k.trackId)??P.set(k.trackId,[]).get(k.trackId)).push(k);for(let[k,M]of P){if(ae.has(k)||b>=Qh||h){Te.push(...M);continue}for(let H=0;H<M.length;H+=ne)pa(M.slice(H,H+ne))}Te.length&&!Ee&&(Ee=setTimeout(Td,ZT),Ee.unref?.())}let kI=N=>{let P=new Map;for(let{trackId:k,event:M}of N)(P.get(k)??P.set(k,[]).get(k)).push(M);return[...P].map(([k,M])=>({trackId:k,events:M}))},jo=(N,P,k,{progress:M})=>{let H=Date.now();for(let{event:pe}of N){let Se=ce.get(pe.id);Se&&(Se.nacks++,Se.retryAt=H+Math.max(1e3,k||I))}M&&ma()};async function xI(N){let P=new Set(N.map(k=>k.trackId));for(let k of P)ae.add(k);b++;try{let k=await R(`${Y}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:kI(N)}),signal:AbortSignal.timeout(S)}),M=(Number(k.headers.get("retry-after"))||0)*1e3;if(k.status===200){let H=await k.json();ne=Nr;for(let pe of H.results??[]){vd(pe.acked??[]);let Se=(Pe,pt)=>{let $t=new Map;for(let Ge of Pe??[])($t.get(Ge.reason)??$t.set(Ge.reason,[]).get(Ge.reason)).push(Ge.id);for(let[Ge,gg]of $t)ug(gg,Ge,pt,H.retryAfterMs??null)};Se(pe.rejected,!0),Se(pe.deferred,!1)}vd(H.discarded??[]),ma();return}if(k.status===429){jo(N,"overloaded",M||5e3,{progress:!0});return}if(k.status===413&&N.length>1){ne=Math.max(1,Math.floor(N.length/2)),E.error(`[plane-link] ingest request too large (${N.length} events) \u2014 halving batches to ${ne}`),jo(N,"too_large",1e3,{progress:!0});return}if(k.status===413){let H=ce.get(N[0].event.id);H&&(ce.delete(H.event.id),lg(H,"too_large"),(we[H.trackId]??0)<H.seq&&(we[H.trackId]=H.seq),xd(H.trackId)),ma();return}if(k.status===401||k.status===403){if(Date.now()-Ac>=QT){Ac=Date.now();let H="";try{H=(await k.json())?.error??""}catch{}E.error(`[plane-link] plane refused ingest (HTTP ${k.status}${H?`: ${H}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}jo(N,`http_${k.status}`,6e4,{progress:!1});return}if(k.status===400){E.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${N.length} event(s) for retry; this is a bug, not data`),jo(N,"http_400",6e4,{progress:!1});return}jo(N,`http_${k.status}`,M||5e3,{progress:!1})}catch(k){let M=k?.name==="TimeoutError"||k?.name==="AbortError";M&&N.length>1&&(ne=Math.max(1,Math.floor(N.length/2)),E.error(`[plane-link] ingest request of ${N.length} event(s) outlived its ${S} ms deadline \u2014 halving batches to ${ne}`)),jo(N,M?"timeout":"network",5e3,{progress:!1})}finally{b--;for(let k of P)ae.delete(k)}}async function vI(){if(Ie!=="http"||!h||!bt||_)return;let N=Date.now();if(!(g>N)){_=!0;try{let P=await R(`${Y}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(S)});if(!P.ok)throw new Error(`HTTP ${P.status}`);E.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),h=!1,g=0,ye=Date.now()+S+I+1e3;for(let k of ce.values())k.retryAt=Math.min(k.retryAt,Date.now());Sd()}catch(P){B++;let k=Math.min(A*2**(B-1),XT);g=Date.now()+k,E.error(`[plane-link] ingest door still not answering (${P?.message||P}); next probe in ${Math.round(k/1e3)}s`)}finally{_=!1}}}function pg(){let N=Date.now();return{mode:Ie,unacked:ce.size,oldestUnackedAgeMs:dg(N),lastProgressAt:V,degradedSince:ge,stallRedials:B,deadLettered:be,http:Ie==="http"?{inFlight:b,circuitOpen:h,nextProbeInMs:h?Math.max(0,g-N):0,batchCap:ne}:null}}async function mg(){if(!i)return;let N;try{N=await i()}catch(P){E.error("[plane-link] metrics sample failed:",P?.message||P);return}N&&Ze({v:et,ch:"ctl",type:"metrics",metrics:{...N,planeLink:pg()}})}function SI(){i&&(Sn&&clearInterval(Sn),mg(),Sn=setInterval(mg,c),Sn.unref?.())}async function TI(){let N;try{N=await o()}catch(P){E.error("[plane-link] backfill listTracks failed:",P?.message||P);return}Ze({v:et,ch:"ctl",type:"tracks",ids:N.filter(P=>!P.transferredTo).map(P=>P.id).filter(P=>typeof P=="string")});for(let P of N){if(!bt)return;let k;try{k=await F(P)}catch{continue}let M=te[P.id]??0,H=k.filter(Se=>Se.seq>M&&typeof Se.id=="string"),pe=Ie==="http"?ne:Nr;for(let Se=0;Se<H.length;Se+=pe){if(!await bI(P.id))return;let Pe=H.slice(Se,Se+Math.min(pe,Ie==="http"?ne:Nr)).map(pt=>({trackId:P.id,event:pt}));if(!pa(Pe))return}}}function hg(N){!bt||!N?.trackId||typeof N.id!="string"||(Te.push({trackId:N.trackId,event:N}),Ee||(Ee=setTimeout(Td,ZT),Ee.unref?.()))}let ha=new Map;async function AI(N){let{reqId:P,method:k,path:M,headers:H={},body:pe}=N,Se=Object.fromEntries(Object.entries(H).filter(([,pt])=>pt!=null)),Pe=new AbortController;ha.set(P,Pe);try{if(N.host){await EI(N,Se,Pe);return}let pt=await fetch(m+M,{method:k,headers:Se,body:pe?Buffer.from(pe,"base64"):void 0,signal:Pe.signal}),$t=Object.fromEntries(pt.headers);for(let Ge of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete $t[Ge];if($t["content-type"]||($t["content-type"]="application/octet-stream"),Ze({v:et,ch:"control",type:"http_head",reqId:P,status:pt.status,headers:$t}),pt.body){for await(let Ge of pt.body)if(!Ze({v:et,ch:"control",type:"http_data",reqId:P,data:Buffer.from(Ge).toString("base64")}))break}Ze({v:et,ch:"control",type:"http_end",reqId:P,error:null})}finally{ha.delete(P)}}function EI(N,P,k){let{reqId:M,method:H,path:pe,body:Se}=N,Pe=new URL(m);return new Promise(pt=>{let $t=Kj.request({host:Pe.hostname,port:Pe.port,method:H,path:pe,headers:{...P,host:N.host},signal:k.signal},Ge=>{let{connection:gg,"keep-alive":$H,"transfer-encoding":RH,upgrade:OH,..._I}=Ge.headers;Ze({v:et,ch:"control",type:"http_head",reqId:M,status:Ge.statusCode||502,headers:_I}),Ge.on("data",II=>{Ze({v:et,ch:"control",type:"http_data",reqId:M,data:Buffer.from(II).toString("base64")})||Ge.destroy()}),Ge.on("end",()=>{Ze({v:et,ch:"control",type:"http_end",reqId:M,error:null}),pt()}),Ge.on("error",()=>{Ze({v:et,ch:"control",type:"http_end",reqId:M,error:"view upstream error"}),pt()})});$t.on("error",Ge=>{Ze({v:et,ch:"control",type:"http_end",reqId:M,error:Ge?.message||"view request failed"}),pt()}),Se&&$t.write(Buffer.from(Se,"base64")),$t.end()})}function wg(){if(bd)return;Le=new Zh(e,{headers:{authorization:`Bearer ${t}`}}),Le.on("open",async()=>{let P=null;try{P=await s?.()||null}catch{P=null}Ze({v:et,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Yt,installKind:p,...typeof f=="function"?{updatePolicy:f()}:f?{updatePolicy:f}:{},providers:Vj(),...P?{agents:P}:{}})}),Le.on("message",P=>{let k;try{k=JSON.parse(P)}catch{return}if(k.ch==="ctl"&&k.type==="hello_ok"){bt=!0,da=JT,fa=null,E.log(`[plane-link] connected to plane as ${n}`),k.minNodeVersion&&Yt&&Qi(Yt,k.minNodeVersion)&&E.error(`[plane-link] this node is bostrat-node ${Yt}; the plane asks for \u2265${k.minNodeVersion} \u2014 update with: ${Nu(p)}`);try{u?.(typeof k.updatePolicy=="string"?k.updatePolicy:null)}catch{}let M=typeof k.latestNodeVersion=="string"?k.latestNodeVersion:null;if(M&&Yt&&Qi(Yt,M)&&!U.has(M)){U.add(M),E.log(`[plane-link] bostrat-node ${M} is available (this node runs ${Yt}) \u2014 ${Nu(p)}`);try{l?.({current:Yt,latest:M,min:k.minNodeVersion??null,installKind:p,required:!!(k.minNodeVersion&&Qi(Yt,k.minNodeVersion))})}catch(pe){E.error(`[plane-link] update notice failed: ${pe?.message||pe}`)}}let H=k.ingest==="http";Ie=x==="ws"?"ws":x==="http"||H?"http":"ws",x==="http"&&!H&&E.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),Ie==="http"&&E.log("[plane-link] spine rides the HTTPS ingest door"),h=!1,g=0,ne=Nr,vn&&clearInterval(vn),vn=setInterval(()=>{Ze({v:et,ch:"ctl",type:"ping"}),fg(),vI().catch(()=>{}),Sd(),Te.length&&!Ee&&Td()},$),vn.unref?.(),SI(),TI().catch(pe=>E.error("[plane-link] backfill failed:",pe?.message||pe));return}if(k.ch==="ctl"&&k.type==="hello_refused"){let M=typeof k.minNodeVersion=="string"?k.minNodeVersion:null,H=typeof k.latestNodeVersion=="string"?k.latestNodeVersion:null;E.error(`[plane-link] plane REFUSED this node: bostrat-node ${Yt??"?"} is below its minimum ${M??"?"} \u2014 ${k.fix||Nu(p)}`),da=YT,fa=0;try{d?.({current:Yt,min:M,latest:H,fix:k.fix||Nu(p),installKind:p})}catch(pe){E.error(`[plane-link] refusal handler failed: ${pe?.message||pe}`)}return}if(k.ch==="ctl"&&k.type==="update_policy"){try{u?.(typeof k.mode=="string"?k.mode:null)}catch{}return}if(k.ch==="ctl"&&k.type==="usage_caps"){Ol(k.caps??{},{source:"plane",updatedAt:k.updatedAt??Date.now()}).then(M=>E.log(`[plane-link] spend caps ${Object.keys(M.caps).length?`updated (${Object.keys(M.caps).join(", ")})`:"cleared"} from plane`)).catch(M=>E.error("[plane-link] spend-caps push rejected:",M?.message||M));return}if(k.ch==="ctl"&&k.type==="transferred"&&Array.isArray(k.tracks)){Promise.resolve().then(()=>a?.(k.tracks)).catch(M=>E.error("[plane-link] transfer notice failed:",M?.message||M));return}if(k.ch==="events"&&k.type==="ack"&&Array.isArray(k.ids)){vd(k.ids);return}if(k.ch==="events"&&k.type==="nack"&&Array.isArray(k.ids)){ug(k.ids,k.reason,k.permanent===!0,k.retryAfterMs??null);return}if(k.ch==="control"&&k.type==="http_open"){AI(k).catch(M=>Ze({v:et,ch:"control",type:"http_end",reqId:k.reqId,error:M?.message||"tunnel error"}));return}if(k.ch==="control"&&k.type==="http_cancel"){ha.get(k.reqId)?.abort();return}if(k.ch==="attach"&&k.type==="open"){let M=new Zh(L+k.path,Array.isArray(k.protocols)?k.protocols:[],k.host?{headers:{host:k.host}}:void 0);se.set(k.streamId,M),G.set(k.streamId,[]),M.on("open",()=>{let pe=G.get(k.streamId)||[];G.delete(k.streamId);for(let{buf:Se,bin:Pe}of pe)M.send(Se,{binary:Pe})}),M.on("message",(pe,Se)=>{let Pe=Se?pe:Buffer.from(String(pe));Ze({v:et,ch:"attach",type:"data",streamId:k.streamId,data:Pe.toString("base64"),...Se?{bin:!0}:{}})});let H=()=>{G.delete(k.streamId),se.delete(k.streamId)&&Ze({v:et,ch:"attach",type:"close",streamId:k.streamId})};M.on("close",H),M.on("error",H);return}if(k.ch==="attach"&&k.type==="data"){let M=se.get(k.streamId);if(!M)return;let H=Buffer.from(k.data,"base64"),pe=k.bin===!0,Se=G.get(k.streamId);Se?Se.push({buf:H,bin:pe}):M.send(H,{binary:pe});return}if(k.ch==="attach"&&k.type==="close"){let M=se.get(k.streamId);se.delete(k.streamId),G.delete(k.streamId),M?.close();return}});let N=()=>{bt&&E.log("[plane-link] disconnected from plane"),bt=!1,vn&&(clearInterval(vn),vn=null),Sn&&(clearInterval(Sn),Sn=null),ce.clear(),we={},Te=[],Ee&&(clearTimeout(Ee),Ee=null),ae.clear();for(let M of se.values())M.close();se.clear(),G.clear();for(let M of ha.values())M.abort();if(ha.clear(),bd||Do)return;fa==null&&(fa=Date.now());let P=Date.now()-fa<zj,k=Math.max(P?Jj+Math.random()*Yj:da,ye-Date.now());Do=setTimeout(()=>{Do=null,wg()},k),Do.unref?.(),P||(da=Math.min(da*2,YT))};Le.on("close",N),Le.on("error",P=>{let k=/Unexpected server response: (\d+)/.exec(P?.message||"")?.[1];(k==="401"||k==="403")&&Date.now()-Ac>=QT&&(Ac=Date.now(),E.error(`[plane-link] plane refused this node's credentials (HTTP ${k}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),N()})}return ht.on("event",hg),wI().then(wg),{stop:()=>{bd=!0,ht.off("event",hg),Do&&clearTimeout(Do),Bo&&clearTimeout(Bo),vn&&clearInterval(vn),Sn&&clearInterval(Sn),Ee&&clearTimeout(Ee),Ze({v:et,ch:"ctl",type:"bye",reason:"shutdown"}),Le?.close()},status:()=>({connected:bt,nodeId:n,cursor:{...te},inflight:ce.size,...pg()}),_tick:()=>{fg(),Sd()}}}sl();var Du=(()=>{try{return iL(import.meta.url)("../package.json").version||null}catch{return null}})(),cL=process.env.BOSTRAT_STATE_DIR||tt.join(process.env.HOME||sL.tmpdir(),".bostrat"),oA=tt.join(cL,"self-update"),Bu=tt.join(oA,"status.json"),lL=1440*6e4,uL=6e4,ec=new Set(["npm-global","git-checkout"]);function sA({kind:e=_o(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:ec.has(e)?e==="git-checkout"||dL(t)?"auto":"prompt":"off"}function dL(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var _t=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function fL({kind:e,to:t,from:n=Du,statusFile:r=Bu,logFile:o=tt.join(oA,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||tt.resolve(tt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!ec.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(y,x="")=>`write_status ${_t(y)} ${x}`,f=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${_t(i)}`,p=`
|
|
592
|
+
`);fL=new Set(["claude","codex","cursor"]);zi=new Set,pL=e=>zi.has(e);mL=9e4;VT={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};bh=new Map;Eh=120;Qh=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:va(e.hookToken),createdAt:Date.now()});ea=new Map,ra=e=>ea.get(e)||(qt?he(qt,e)?.handoff:null)||null;Ah=6e4,nn=(e,t,n=15e3)=>XT("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),_h=new Map,tw=e=>_h.get(e)||null;kh=new Map;xL=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,vL=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ta=J.join(En,"worktrees"),zs=[Wi,Ru],Nt=new Map,SL=e=>Nt.get(e)||(qt?qt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,jo=e=>{let t=new Map([...Nt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=qt?he(qt,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},qu=e=>({id:e.id,trackId:e.trackId,trackName:e.trackName,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,reason:e.reason||null,step:e.step,failedStep:e.failedStep||null,box:e.box,branch:e.branch,base:e.base,repos:e.repos||null,dir:e.dir,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt});oc=e=>gE(e.trackId,e.id,qu(e));iw=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Ji=class extends Error{}});lt();import xe from"node:path";import mt from"node:fs";import XH from"node:readline";import{fileURLToPath as ZH}from"node:url";import Gt from"express";import QH from"node:http";import eW from"node:https";import _d from"node:os";import{WebSocketServer as tW}from"ws";lt();import wO from"node-pty";async function Xy(e,t,n={}){if(!Pe(t)){ul(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await fr()){ul(e,`host tmux server not running on ${$t}`),e.close(1011,"tmux server unavailable");return}if(!await ge(t)){ul(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Jy(n.cols,120,Zy),o=Jy(n.rows,32,Qy),s;try{s=wO.spawn(K,["-S",$t,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ul(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&mf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(mf(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&kO(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),e.on("close",()=>{try{s.kill()}catch{}bO(t)})}var gO=6,yO=250;async function bO(e,t=gO,n=yO){for(let r=0;r<t;r++){if(await oo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Zy=20,Qy=6;function kO(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Zy&&Number(t)>=Qy}function Jy(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function mf(e,t){try{e.send(JSON.stringify(t))}catch{}}function ul(e,t){mf(e,{type:"error",message:t})}function eb(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}Xt();fl();op();Nr();import{createRequire as nW}from"node:module";var ML=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function dw({tracks:e=[],views:t=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of e)!o||o.archivedAt&&!o.archiving||o.transferredTo||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(ML)&&r.push(`merge-back:${o.id}`));return t.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}import NE from"node:fs";import Hr from"node:fs/promises";import JL from"node:os";import st from"node:path";import{spawn as XL}from"node:child_process";import{createRequire as ZL}from"node:module";me();Fl();import OE from"node:fs";import ac from"node:fs/promises";import DL from"node:http";import on from"node:path";import Vu from"node:os";import{createRequire as BL}from"node:module";import pw from"ws";import NL from"node:path";import PL from"node:os";var sc=process.env.BOSTRAT_STATE_DIR||NL.join(process.env.HOME||PL.tmpdir(),".bostrat");function fw(e=process.env){let t=(e.BOSTRAT_HOST||"").trim();if(t!=="all")return t||(e.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function SE(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=fw(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function TE(e){let t=String(e||"").trim();if(!t)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`wss://${t}`,r;try{r=new URL(n)}catch{return t}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):t}var rn=(()=>{try{return BL(import.meta.url)("../package.json").version||null}catch{return null}})(),EE=process.env.BOSTRAT_STATE_DIR||on.join(process.env.HOME||Vu.tmpdir(),".bostrat");function Lo({env:e=process.env,pkgDir:t=on.resolve(on.dirname(new URL(import.meta.url).pathname),".."),exists:n=OE.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(on.join(t,"..",".git"))?"git-checkout":"dev"}function ic(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function Gu(e){switch(e){case"npm-global":return"npm install -g bostrat-node && restart the bostrat-node service";case"git-checkout":return"scripts/deploy-pi.sh (or The Bakery \u2192 Deploy to Pi \u2192 bostrat)";case"container":return"Links \u2192 Fly \u2192 Update (or the plane's auto-update while the node sleeps)";default:return"update this node's bostrat-node checkout/install"}}function jL({home:e=process.env.HOME||Vu.homedir(),env:t=process.env}={}){let n=r=>{try{return OE.statSync(r),!0}catch{return!1}};return{claude:n(on.join(e,".claude",".credentials.json")),codex:n(on.join(t.CODEX_HOME||on.join(e,".codex"),"auth.json"))}}var ot=1,AE=1e3,_E=3e4,LL=6e4,FL=1500,UL=500,Ur=100,HL=2e3,WL=25e3,qL=6e4,CE=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},KL=CE(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),GL=3e4,IE=10*6e4,VL=400,$E=250,zL=CE(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),mw=4,YL=6e4,RE=6e4;function ME({url:e,token:t,nodeId:n=Vu.hostname(),name:r=Vu.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=YL,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:f=null,updatePolicy:d=null,installKind:p=Lo(),selfUrl:h=`http://127.0.0.1:${process.env.PORT||3362}`,onDegraded:w=null,onRecovered:g=null,ingestMode:k=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:S=KL,unackedRetryMs:E=qL,pingMs:R=WL,maxInflightEvents:B=VL,httpTimeoutMs:T=zL,stallBackoffMinMs:_=GL,fetchImpl:O=globalThis.fetch,log:I=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=TE(e);let U=h.replace(/^http/,"ws"),H=new Set,ae=new Map,X=new Map,C=on.join(EE,"plane-link","cursor.json"),Q=on.join(EE,"plane-link","dead-letter.ndjson"),ve=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),W={},be={},se=new Map,pe=null,ie=null,L=0,ce=0,ue=0,Ae=Promise.resolve(),_e="ws",xt=0,je=!1,A=0,G=!1,Ge=Ur,_t=new Set,at=[],nt=null,y=null,m=!1,b=!1,M=AE,ne=null,de=null,De=null,ht=null,Re=null,ya=!1,Mc=0;async function VI(){try{W=JSON.parse(await ac.readFile(C,"utf8"))}catch{W={}}}function zI(){ya=!0,!De&&(De=setTimeout(async()=>{if(De=null,!!ya){ya=!1;try{await ac.mkdir(on.dirname(C),{recursive:!0}),await ac.writeFile(C,JSON.stringify(W))}catch(P){I.error("[plane-link] cursor flush failed:",P?.message||P)}}},HL),De.unref?.())}function rt(P){return!y||y.readyState!==pw.OPEN?!1:(y.send(JSON.stringify(P)),!0)}function ba(P){let D=Date.now();for(let{trackId:x,event:N}of P){let q=se.get(N.id);se.set(N.id,{trackId:x,seq:N.seq,event:N,sentAt:D,firstSentAt:q?.firstSentAt??D,retryAt:D+E,tries:(q?.tries??0)+1,nacks:q?.nacks??0})}return _e==="http"?(ZI(P),!0):rt({v:ot,ch:"events",type:"append",events:P})}function YI(P){let D=null;for(let x of se.values())x.trackId===P&&(D==null||x.seq<D)&&(D=x.seq);return D}function Od(P){let D=be[P];if(D==null)return;let x=YI(P),N=x==null?D:Math.min(D,x-1);N>(W[P]??0)&&(W[P]=N,zI())}function ka(){if(pe=Date.now(),ie!=null){let P=Date.now()-ie;I.log(`[plane-link] spine sync recovered after ${Math.round(P/1e3)}s degraded (${L} stall redial(s))`);let D=ie;ie=null,L=0,ce=0,je=!1,A=0;try{g?.({since:D,downMs:P})}catch(x){I.error(`[plane-link] onRecovered failed: ${x?.message||x}`)}}}function Cd(P){if(!Array.isArray(P)||!P.length)return;let D=new Set;for(let x of P){let N=se.get(x);N&&(se.delete(x),(be[N.trackId]??0)<N.seq&&(be[N.trackId]=N.seq),D.add(N.trackId))}for(let x of D)Od(x);ka()}function Eg(P,D){ue++;let x={at:Date.now(),trackId:P.trackId,seq:P.seq,id:P.event?.id??null,type:P.event?.type??null,reason:D,event:P.event??null};I.error(`[plane-link] DEAD-LETTER ${P.trackId} seq ${P.seq} (${x.type??"?"}): plane rejected it permanently (${D}) \u2014 kept in ${Q}`),Ae=Ae.then(async()=>{await ac.mkdir(on.dirname(Q),{recursive:!0}),await ac.appendFile(Q,JSON.stringify(x)+`
|
|
593
|
+
`)}).catch(N=>I.error(`[plane-link] dead-letter write failed: ${N?.message||N}`))}function Ag(P,D,x,N=null){if(!Array.isArray(P)||!P.length)return;let q=new Set,ye=Date.now();for(let Ee of P){let Be=se.get(Ee);Be&&(x?(se.delete(Ee),Eg(Be,D),(be[Be.trackId]??0)<Be.seq&&(be[Be.trackId]=Be.seq),q.add(Be.trackId)):(Be.nacks++,Be.retryAt=ye+Math.max(1e3,Number(N)||E)))}for(let Ee of q)Od(Ee);ka()}function _g(P=Date.now()){let D=1/0;for(let x of se.values())x.firstSentAt<D&&(D=x.firstSentAt);return D===1/0?0:P-D}function Ig(){if(!m||!se.size)return;let P=Date.now(),D=_g(P);if(D<S||pe!=null&&P-pe<S||_e==="http"&&je||ce>P)return;L++;let x=Math.min(_*2**(L-1),IE);if(ie==null){ie=P-D,I.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(D/1e3)}s with ${se.size} event(s) in flight (${_e} door) \u2014 the plane is not answering appends`);try{w?.({since:ie,unacked:se.size,oldestAgeMs:D,mode:_e})}catch(N){I.error(`[plane-link] onDegraded failed: ${N?.message||N}`)}}if(_e==="http"){je=!0,A=P+x,I.error(`[plane-link] ingest circuit OPEN (stall #${L}); probing /v1/ingest/health in ${Math.round(x/1e3)}s`);return}ce=P+x,I.error(`[plane-link] closing the link and redialing in ${Math.round(x/1e3)}s (stall redial #${L})`);try{y?.terminate()}catch{}}function Md(){if(!m)return;let P=Date.now();if(_e==="http"&&je)return;let D=[...se.values()].filter(N=>N.event&&N.retryAt<=P&&!(_e==="http"&&_t.has(N.trackId)));if(!D.length)return;D.sort((N,q)=>N.trackId===q.trackId?N.seq-q.seq:N.trackId<q.trackId?-1:1),I.log(`[plane-link] re-sending ${D.length} unacked event(s) the plane never confirmed`);let x=_e==="http"?Ge:Ur;if(_e==="http"){let N=new Map;for(let q of D)(N.get(q.trackId)??N.set(q.trackId,[]).get(q.trackId)).push(q);for(let[,q]of N){if(xt>=mw)return;ba(q.slice(0,x).map(ye=>({trackId:ye.trackId,event:ye.event})))}return}for(let N=0;N<D.length;N+=x){let q=D.slice(N,N+x).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!ba(q))return}}async function JI(P=null){for(;;){if(!m)return!1;let D=se.size<B,x=_e!=="http"||xt<mw&&!je&&(!P||!_t.has(P));if(D&&x)return!0;await new Promise(N=>setTimeout(N,100))}}function Nd(){if(nt=null,!m||!at.length)return;let P=at;if(at=[],_e!=="http"){for(let x=0;x<P.length;x+=Ur)ba(P.slice(x,x+Ur));return}let D=new Map;for(let x of P)(D.get(x.trackId)??D.set(x.trackId,[]).get(x.trackId)).push(x);for(let[x,N]of D){if(_t.has(x)||xt>=mw||je){at.push(...N);continue}for(let q=0;q<N.length;q+=Ge)ba(N.slice(q,q+Ge))}at.length&&!nt&&(nt=setTimeout(Nd,$E),nt.unref?.())}let XI=P=>{let D=new Map;for(let{trackId:x,event:N}of P)(D.get(x)??D.set(x,[]).get(x)).push(N);return[...D].map(([x,N])=>({trackId:x,events:N}))},zo=(P,D,x,{progress:N})=>{let q=Date.now();for(let{event:ye}of P){let Ee=se.get(ye.id);Ee&&(Ee.nacks++,Ee.retryAt=q+Math.max(1e3,x||E))}N&&ka()};async function ZI(P){let D=new Set(P.map(x=>x.trackId));for(let x of D)_t.add(x);xt++;try{let x=await O(`${ve}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:XI(P)}),signal:AbortSignal.timeout(T)}),N=(Number(x.headers.get("retry-after"))||0)*1e3;if(x.status===200){let q=await x.json();Ge=Ur;for(let ye of q.results??[]){Cd(ye.acked??[]);let Ee=(Be,vt)=>{let Lt=new Map;for(let Ye of Be??[])(Lt.get(Ye.reason)??Lt.set(Ye.reason,[]).get(Ye.reason)).push(Ye.id);for(let[Ye,Mg]of Lt)Ag(Mg,Ye,vt,q.retryAfterMs??null)};Ee(ye.rejected,!0),Ee(ye.deferred,!1)}Cd(q.discarded??[]),ka();return}if(x.status===429){zo(P,"overloaded",N||5e3,{progress:!0});return}if(x.status===413&&P.length>1){Ge=Math.max(1,Math.floor(P.length/2)),I.error(`[plane-link] ingest request too large (${P.length} events) \u2014 halving batches to ${Ge}`),zo(P,"too_large",1e3,{progress:!0});return}if(x.status===413){let q=se.get(P[0].event.id);q&&(se.delete(q.event.id),Eg(q,"too_large"),(be[q.trackId]??0)<q.seq&&(be[q.trackId]=q.seq),Od(q.trackId)),ka();return}if(x.status===401||x.status===403){if(Date.now()-Mc>=RE){Mc=Date.now();let q="";try{q=(await x.json())?.error??""}catch{}I.error(`[plane-link] plane refused ingest (HTTP ${x.status}${q?`: ${q}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}zo(P,`http_${x.status}`,6e4,{progress:!1});return}if(x.status===400){I.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${P.length} event(s) for retry; this is a bug, not data`),zo(P,"http_400",6e4,{progress:!1});return}zo(P,`http_${x.status}`,N||5e3,{progress:!1})}catch(x){let N=x?.name==="TimeoutError"||x?.name==="AbortError";N&&P.length>1&&(Ge=Math.max(1,Math.floor(P.length/2)),I.error(`[plane-link] ingest request of ${P.length} event(s) outlived its ${T} ms deadline \u2014 halving batches to ${Ge}`)),zo(P,N?"timeout":"network",5e3,{progress:!1})}finally{xt--;for(let x of D)_t.delete(x)}}async function QI(){if(_e!=="http"||!je||!m||G)return;let P=Date.now();if(!(A>P)){G=!0;try{let D=await O(`${ve}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(T)});if(!D.ok)throw new Error(`HTTP ${D.status}`);I.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),je=!1,A=0,ce=Date.now()+T+E+1e3;for(let x of se.values())x.retryAt=Math.min(x.retryAt,Date.now());Md()}catch(D){L++;let x=Math.min(_*2**(L-1),IE);A=Date.now()+x,I.error(`[plane-link] ingest door still not answering (${D?.message||D}); next probe in ${Math.round(x/1e3)}s`)}finally{G=!1}}}function $g(){let P=Date.now();return{mode:_e,unacked:se.size,oldestUnackedAgeMs:_g(P),lastProgressAt:pe,degradedSince:ie,stallRedials:L,deadLettered:ue,http:_e==="http"?{inFlight:xt,circuitOpen:je,nextProbeInMs:je?Math.max(0,A-P):0,batchCap:Ge}:null}}async function Rg(){if(!i)return;let P;try{P=await i()}catch(D){I.error("[plane-link] metrics sample failed:",D?.message||D);return}P&&rt({v:ot,ch:"ctl",type:"metrics",metrics:{...P,planeLink:$g()}})}function e$(){i&&(Re&&clearInterval(Re),Rg(),Re=setInterval(Rg,c),Re.unref?.())}async function t$(){let P;try{P=await o()}catch(D){I.error("[plane-link] backfill listTracks failed:",D?.message||D);return}rt({v:ot,ch:"ctl",type:"tracks",ids:P.filter(D=>!D.transferredTo).map(D=>D.id).filter(D=>typeof D=="string")});for(let D of P){if(!m)return;let x;try{x=await F(D)}catch{continue}let N=W[D.id]??0,q=x.filter(Ee=>Ee.seq>N&&typeof Ee.id=="string"),ye=_e==="http"?Ge:Ur;for(let Ee=0;Ee<q.length;Ee+=ye){if(!await JI(D.id))return;let Be=q.slice(Ee,Ee+Math.min(ye,_e==="http"?Ge:Ur)).map(vt=>({trackId:D.id,event:vt}));if(!ba(Be))return}}}function Og(P){!m||!P?.trackId||typeof P.id!="string"||(at.push({trackId:P.trackId,event:P}),nt||(nt=setTimeout(Nd,$E),nt.unref?.()))}let xa=new Map;async function n$(P){let{reqId:D,method:x,path:N,headers:q={},body:ye}=P,Ee=Object.fromEntries(Object.entries(q).filter(([,vt])=>vt!=null)),Be=new AbortController;xa.set(D,Be);try{if(P.host){await r$(P,Ee,Be);return}let vt=await fetch(h+N,{method:x,headers:Ee,body:ye?Buffer.from(ye,"base64"):void 0,signal:Be.signal}),Lt=Object.fromEntries(vt.headers);for(let Ye of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Lt[Ye];if(Lt["content-type"]||(Lt["content-type"]="application/octet-stream"),rt({v:ot,ch:"control",type:"http_head",reqId:D,status:vt.status,headers:Lt}),vt.body){for await(let Ye of vt.body)if(!rt({v:ot,ch:"control",type:"http_data",reqId:D,data:Buffer.from(Ye).toString("base64")}))break}rt({v:ot,ch:"control",type:"http_end",reqId:D,error:null})}finally{xa.delete(D)}}function r$(P,D,x){let{reqId:N,method:q,path:ye,body:Ee}=P,Be=new URL(h);return new Promise(vt=>{let Lt=DL.request({host:Be.hostname,port:Be.port,method:q,path:ye,headers:{...D,host:P.host},signal:x.signal},Ye=>{let{connection:Mg,"keep-alive":kW,"transfer-encoding":xW,upgrade:vW,...o$}=Ye.headers;rt({v:ot,ch:"control",type:"http_head",reqId:N,status:Ye.statusCode||502,headers:o$}),Ye.on("data",s$=>{rt({v:ot,ch:"control",type:"http_data",reqId:N,data:Buffer.from(s$).toString("base64")})||Ye.destroy()}),Ye.on("end",()=>{rt({v:ot,ch:"control",type:"http_end",reqId:N,error:null}),vt()}),Ye.on("error",()=>{rt({v:ot,ch:"control",type:"http_end",reqId:N,error:"view upstream error"}),vt()})});Lt.on("error",Ye=>{rt({v:ot,ch:"control",type:"http_end",reqId:N,error:Ye?.message||"view request failed"}),vt()}),Ee&&Lt.write(Buffer.from(Ee,"base64")),Lt.end()})}function Cg(){if(b)return;y=new pw(e,{headers:{authorization:`Bearer ${t}`}}),y.on("open",async()=>{let D=null;try{D=await s?.()||null}catch{D=null}rt({v:ot,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:rn,installKind:p,...typeof d=="function"?{updatePolicy:d()}:d?{updatePolicy:d}:{},providers:jL(),...D?{agents:D}:{}})}),y.on("message",D=>{let x;try{x=JSON.parse(D)}catch{return}if(x.ch==="ctl"&&x.type==="hello_ok"){m=!0,M=AE,ne=null,I.log(`[plane-link] connected to plane as ${n}`),x.minNodeVersion&&rn&&ic(rn,x.minNodeVersion)&&I.error(`[plane-link] this node is bostrat-node ${rn}; the plane asks for \u2265${x.minNodeVersion} \u2014 update with: ${Gu(p)}`);try{u?.(typeof x.updatePolicy=="string"?x.updatePolicy:null)}catch{}let N=typeof x.latestNodeVersion=="string"?x.latestNodeVersion:null;if(N&&rn&&ic(rn,N)&&!H.has(N)){H.add(N),I.log(`[plane-link] bostrat-node ${N} is available (this node runs ${rn}) \u2014 ${Gu(p)}`);try{l?.({current:rn,latest:N,min:x.minNodeVersion??null,installKind:p,required:!!(x.minNodeVersion&&ic(rn,x.minNodeVersion))})}catch(ye){I.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}let q=x.ingest==="http";_e=k==="ws"?"ws":k==="http"||q?"http":"ws",k==="http"&&!q&&I.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),_e==="http"&&I.log("[plane-link] spine rides the HTTPS ingest door"),je=!1,A=0,Ge=Ur,ht&&clearInterval(ht),ht=setInterval(()=>{rt({v:ot,ch:"ctl",type:"ping"}),Ig(),QI().catch(()=>{}),Md(),at.length&&!nt&&Nd()},R),ht.unref?.(),e$(),t$().catch(ye=>I.error("[plane-link] backfill failed:",ye?.message||ye));return}if(x.ch==="ctl"&&x.type==="hello_refused"){let N=typeof x.minNodeVersion=="string"?x.minNodeVersion:null,q=typeof x.latestNodeVersion=="string"?x.latestNodeVersion:null;I.error(`[plane-link] plane REFUSED this node: bostrat-node ${rn??"?"} is below its minimum ${N??"?"} \u2014 ${x.fix||Gu(p)}`),M=_E,ne=0;try{f?.({current:rn,min:N,latest:q,fix:x.fix||Gu(p),installKind:p})}catch(ye){I.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}return}if(x.ch==="ctl"&&x.type==="update_policy"){try{u?.(typeof x.mode=="string"?x.mode:null)}catch{}return}if(x.ch==="ctl"&&x.type==="usage_caps"){Ll(x.caps??{},{source:"plane",updatedAt:x.updatedAt??Date.now()}).then(N=>I.log(`[plane-link] spend caps ${Object.keys(N.caps).length?`updated (${Object.keys(N.caps).join(", ")})`:"cleared"} from plane`)).catch(N=>I.error("[plane-link] spend-caps push rejected:",N?.message||N));return}if(x.ch==="ctl"&&x.type==="transferred"&&Array.isArray(x.tracks)){Promise.resolve().then(()=>a?.(x.tracks)).catch(N=>I.error("[plane-link] transfer notice failed:",N?.message||N));return}if(x.ch==="events"&&x.type==="ack"&&Array.isArray(x.ids)){Cd(x.ids);return}if(x.ch==="events"&&x.type==="nack"&&Array.isArray(x.ids)){Ag(x.ids,x.reason,x.permanent===!0,x.retryAfterMs??null);return}if(x.ch==="control"&&x.type==="http_open"){n$(x).catch(N=>rt({v:ot,ch:"control",type:"http_end",reqId:x.reqId,error:N?.message||"tunnel error"}));return}if(x.ch==="control"&&x.type==="http_cancel"){xa.get(x.reqId)?.abort();return}if(x.ch==="attach"&&x.type==="open"){let N=new pw(U+x.path,Array.isArray(x.protocols)?x.protocols:[],x.host?{headers:{host:x.host}}:void 0);ae.set(x.streamId,N),X.set(x.streamId,[]),N.on("open",()=>{let ye=X.get(x.streamId)||[];X.delete(x.streamId);for(let{buf:Ee,bin:Be}of ye)N.send(Ee,{binary:Be})}),N.on("message",(ye,Ee)=>{let Be=Ee?ye:Buffer.from(String(ye));rt({v:ot,ch:"attach",type:"data",streamId:x.streamId,data:Be.toString("base64"),...Ee?{bin:!0}:{}})});let q=()=>{X.delete(x.streamId),ae.delete(x.streamId)&&rt({v:ot,ch:"attach",type:"close",streamId:x.streamId})};N.on("close",q),N.on("error",q);return}if(x.ch==="attach"&&x.type==="data"){let N=ae.get(x.streamId);if(!N)return;let q=Buffer.from(x.data,"base64"),ye=x.bin===!0,Ee=X.get(x.streamId);Ee?Ee.push({buf:q,bin:ye}):N.send(q,{binary:ye});return}if(x.ch==="attach"&&x.type==="close"){let N=ae.get(x.streamId);ae.delete(x.streamId),X.delete(x.streamId),N?.close();return}});let P=()=>{m&&I.log("[plane-link] disconnected from plane"),m=!1,ht&&(clearInterval(ht),ht=null),Re&&(clearInterval(Re),Re=null),se.clear(),be={},at=[],nt&&(clearTimeout(nt),nt=null),_t.clear();for(let N of ae.values())N.close();ae.clear(),X.clear();for(let N of xa.values())N.abort();if(xa.clear(),b||de)return;ne==null&&(ne=Date.now());let D=Date.now()-ne<LL,x=Math.max(D?FL+Math.random()*UL:M,ce-Date.now());de=setTimeout(()=>{de=null,Cg()},x),de.unref?.(),D||(M=Math.min(M*2,_E))};y.on("close",P),y.on("error",D=>{let x=/Unexpected server response: (\d+)/.exec(D?.message||"")?.[1];(x==="401"||x==="403")&&Date.now()-Mc>=RE&&(Mc=Date.now(),I.error(`[plane-link] plane refused this node's credentials (HTTP ${x}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),P()})}return ct.on("event",Og),VI().then(Cg),{stop:()=>{b=!0,ct.off("event",Og),de&&clearTimeout(de),De&&clearTimeout(De),ht&&clearInterval(ht),Re&&clearInterval(Re),nt&&clearTimeout(nt),rt({v:ot,ch:"ctl",type:"bye",reason:"shutdown"}),y?.close()},status:()=>({connected:m,nodeId:n,cursor:{...W},inflight:se.size,...$g()}),_tick:()=>{Ig(),Md()}}}pl();var zu=(()=>{try{return ZL(import.meta.url)("../package.json").version||null}catch{return null}})(),QL=process.env.BOSTRAT_STATE_DIR||st.join(process.env.HOME||JL.tmpdir(),".bostrat"),PE=st.join(QL,"self-update"),Yu=st.join(PE,"status.json"),eF=1440*6e4,tF=6e4,cc=new Set(["npm-global","git-checkout"]);function DE({kind:e=Lo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:cc.has(e)?e==="git-checkout"||nF(t)?"auto":"prompt":"off"}function nF(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Bt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function rF({kind:e,to:t,from:n=zu,statusFile:r=Yu,logFile:o=st.join(PE,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||st.resolve(st.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!cc.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let f=(g,k="")=>`write_status ${Bt(g)} ${k}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Bt(i)}`,p=`
|
|
555
594
|
health() {
|
|
556
595
|
for i in $(seq 1 45); do
|
|
557
596
|
sleep 1
|
|
558
597
|
if curl -sf -o /dev/null "http://127.0.0.1:${s}/api/health"; then return 0; fi
|
|
559
598
|
done
|
|
560
599
|
return 1
|
|
561
|
-
}`,
|
|
600
|
+
}`,h=`#!/usr/bin/env bash
|
|
562
601
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
563
602
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
564
603
|
set -u
|
|
565
|
-
FROM=${
|
|
566
|
-
TO=${
|
|
567
|
-
STATUS_FILE=${
|
|
604
|
+
FROM=${Bt(n??"")}
|
|
605
|
+
TO=${Bt(t)}
|
|
606
|
+
STATUS_FILE=${Bt(r)}
|
|
568
607
|
STARTED_AT=${Number(u)}
|
|
569
608
|
write_status() { # <status> [error]
|
|
570
609
|
local err="\${2:-}"
|
|
571
610
|
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
572
611
|
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
573
|
-
"$1" "$FROM" "$TO" ${
|
|
612
|
+
"$1" "$FROM" "$TO" ${Bt(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
574
613
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
575
614
|
}
|
|
576
615
|
${p}
|
|
577
|
-
`;if(e==="npm-global"){let
|
|
578
|
-
`:"",
|
|
579
|
-
${
|
|
580
|
-
if ! ${
|
|
581
|
-
${
|
|
616
|
+
`;if(e==="npm-global"){let g=a?st.join(a,"bin","npm"):"npm",k=a?`export PATH=${Bt(st.join(a,"bin"))}:"$PATH"
|
|
617
|
+
`:"",S=a?` --prefix ${Bt(a)}`:"",E=R=>`${Bt(g)} install -g "bostrat-node@${R}" --no-audit --no-fund${S}`;return h+k+`
|
|
618
|
+
${f("updating",'"" ')}
|
|
619
|
+
if ! ${E("$TO")}; then
|
|
620
|
+
${f("failed",'"npm install failed"')}
|
|
582
621
|
exit 1
|
|
583
622
|
fi
|
|
584
|
-
${
|
|
585
|
-
if ! ${
|
|
586
|
-
${
|
|
623
|
+
${f("restarting")}
|
|
624
|
+
if ! ${d}; then
|
|
625
|
+
${f("failed",'"service restart failed"')}
|
|
587
626
|
exit 1
|
|
588
627
|
fi
|
|
589
628
|
if health; then
|
|
@@ -591,89 +630,89 @@ if health; then
|
|
|
591
630
|
exit 0
|
|
592
631
|
fi
|
|
593
632
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
594
|
-
${
|
|
595
|
-
if [ -n "$FROM" ] && ${
|
|
596
|
-
${
|
|
633
|
+
${f("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
634
|
+
if [ -n "$FROM" ] && ${E("$FROM")} && ${d} && health; then
|
|
635
|
+
${f("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
597
636
|
exit 1
|
|
598
637
|
fi
|
|
599
|
-
${
|
|
638
|
+
${f("failed",'"agent did not answer after the update AND the rollback"')}
|
|
600
639
|
exit 1
|
|
601
|
-
`}let w=
|
|
602
|
-
LIVE=${
|
|
640
|
+
`}let w=g=>`( cd ${Bt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return h+`
|
|
641
|
+
LIVE=${Bt(l)}
|
|
603
642
|
TMP_SCRIPT="$(mktemp)"
|
|
604
643
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
605
644
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
606
645
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
607
646
|
# and nothing here may write after that boot or the settle would repeat.
|
|
608
|
-
${
|
|
647
|
+
${f("restarting")}
|
|
609
648
|
if ${w("$TO")}; then
|
|
610
649
|
exit 0
|
|
611
650
|
fi
|
|
612
|
-
${
|
|
651
|
+
${f("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
613
652
|
if [ -n "$FROM" ] && ${w("$FROM")}; then
|
|
614
|
-
${
|
|
653
|
+
${f("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
615
654
|
exit 1
|
|
616
655
|
fi
|
|
617
|
-
${
|
|
656
|
+
${f("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
618
657
|
exit 1
|
|
619
|
-
`}async function tc(e=Bu){try{return JSON.parse(await Pr.readFile(e,"utf8"))}catch{return null}}var pL=new Set(["updating","restarting","rolling-back"]),ju=e=>!!(e&&pL.has(e.status));async function Lu({to:e,from:t=Du,kind:n=_o(),reason:r="manual",statusFile:o=Bu,spawnImpl:s=aL,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!ec.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!Qi(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await tc(o);if(ju(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await Pr.mkdir(tt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?xf(await mL())||tt.resolve(tt.dirname(process.execPath),".."):null,d=fL({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),f=tt.join(tt.dirname(o),`update-${e}.sh`),p=tt.join(tt.dirname(o),"last.log");await Pr.writeFile(f,d,{mode:448}),await Pr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let m=rA.existsSync("/run/systemd/system")&&await hL("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",f]]:["setsid",["-f","bash","-c",`exec bash ${_t(f)} >> ${_t(p)} 2>&1`]];try{s(m[0],m[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await Pr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${w?.message||w}`})),{ok:!1,reason:`could not spawn updater: ${w?.message||w}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:f,statusFile:o}}async function mL(){try{return await Pr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function hL(e){for(let t of String(process.env.PATH||"").split(tt.delimiter))try{return await Pr.access(tt.join(t,e),rA.constants.X_OK),!0}catch{}return!1}var wL=45*6e4;async function aA({statusFile:e=Bu,current:t=Du,now:n=Date.now,staleMs:r=wL}={}){let o=await tc(e);if(!o)return null;let s=a=>Pr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(ju(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function iA({isBusy:e,start:t=Lu,onStuck:n=null,onStarted:r=null,tickMs:o=uL,stuckMs:s=lL,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},f=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let m=u;d();let w=await t({to:m.to,from:m.from,kind:m.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...m,result:w})}catch{}};return{arm({to:p,from:m=Du,kind:w=_o()}){return u?.to===p?!1:(d(),u={to:p,from:m,kind:w,since:a(),stuckFired:!1,timer:i(f,o)},u.timer.unref?.(),!0)},tick:f,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ue();it();var gL=(...e)=>fetch(...e);function yL(e=process.env){return!!($e(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ew(e=process.env){if(!yL(e))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let t=new AbortController,n=setTimeout(()=>t.abort(),1e4);try{let r=await gL(`${$e(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function Xs(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await ew(t),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function Fu(e,t,{source:n="chat"}={}){return O(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ue();it();var bL=(...e)=>fetch(...e);async function Uu(e,t,n,r=process.env){if(!($e(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await bL(`${$e(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function kL(e,t=process.env){return Uu("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function cA({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await kL(t,o):await Xs(e,o),a=await Uu("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function tw(e,t=process.env){let n=await Xs(e,t),r=await Uu("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function nw(e,t=process.env){return(await Uu("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function lA(e,t,n,r=process.env){let{blueprint:o,items:s}=await tw(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await O(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ue();it();var xL=(...e)=>fetch(...e);async function vL(e,t,n,r=process.env){if(!($e(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await xL(`${$e(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var uA=72,rw=160;async function dA(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>uA)throw new Error(`the title is the choice as a LABEL \u2014 ${uA} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${rw} chars) a reader can act on without opening the record`);if(c.length>rw)throw new Error(`the summary is one sentence \u2014 ${rw} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await O(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function fA(e,t=process.env){return vL("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Gm();ue();import ic from"node:fs/promises";import Yu from"node:path";import fF from"node:os";rr();import SL from"node:fs/promises";import pA from"node:path";import TL from"node:os";var mA=()=>pA.join(process.env.BOSTRAT_STATE_DIR||pA.join(process.env.HOME||TL.tmpdir(),".bostrat"),"integrations.json");async function Xn(){try{return JSON.parse(await SL.readFile(mA(),"utf8"))||{}}catch{return{}}}var Hu=["linked","prOpened","prMerged","create"],ow=["prompt","auto","off"],AL={create:["prompt","off"]},hA=e=>AL[e]??ow;function Wu(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Hu){let s=hA(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function Io(){let e=await Xn(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:Wu(e),defaultTeam:n}:null}async function qu(){let e=await Xn(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:Wu(e)}}async function sw(){return Wu(await Xn())}async function wA(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Hu.join(", ")}`);for(let[r,o]of t){if(!Hu.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Hu.join(", ")}`);let s=hA(r);if(!s.includes(o))throw new Error(s.length<ow.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${ow.join(", ")}`)}let n=await Xn();return n.ticketFlow={...Wu(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await wt(mA(),n,{mode:384}),n.ticketFlow}async function nc(){let e=(await Xn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function EL(){let e=(await Xn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var _L={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Ku(){let t=(await Xn()).voiceChat||{},n=t.provider||"openai",r=_L[n];if(!r)return null;let o=n==="openai"?await EL():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function gA(){let e=(await Xn()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function $o(){let e=(await Xn()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}ue();sr();gr();js();import oc from"node:fs/promises";import EA from"node:path";import ZL from"node:os";import{randomUUID as _A}from"node:crypto";var iw={};Ad(iw,{createComment:()=>DL,findIssueUrl:()=>OL,issueStates:()=>LL,mdToAdf:()=>kA,moveIssue:()=>FL,parseIssueRef:()=>RL,resolveIssue:()=>CL,uploadImage:()=>BL,upsertCard:()=>ML,whoami:()=>aw});var Zs=e=>String(e.baseUrl||"").replace(/\/$/,""),bA=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ro(e,t,n,r){let o=await fetch(`${Zs(e)}${n}`,{method:t,headers:{authorization:bA(e),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var IL=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,$L=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function RL(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(IL);return r?r[1].toUpperCase():null}let n=t.match($L);return n?n[1]:null}function OL(e,t=null){let n=null;try{n=t?.baseUrl?new URL(t.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(e||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function CL(e,t){let n;try{n=await Ro(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Zs(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function ML(e,t,{url:n,title:r,subtitle:o}){await Ro(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var NL=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function PL(e){let t=[],n=0;for(let r of e.matchAll(NL)){r.index>n&&t.push({type:"text",text:e.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);t.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?t.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):t.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<e.length&&t.push({type:"text",text:e.slice(n)}),t}function kA(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
620
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
658
|
+
`}async function lc(e=Yu){try{return JSON.parse(await Hr.readFile(e,"utf8"))}catch{return null}}var oF=new Set(["updating","restarting","rolling-back"]),Ju=e=>!!(e&&oF.has(e.status));async function Xu({to:e,from:t=zu,kind:n=Lo(),reason:r="manual",statusFile:o=Yu,spawnImpl:s=XL,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!cc.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!ic(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await lc(o);if(Ju(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await Hr.mkdir(st.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Of(await sF())||st.resolve(st.dirname(process.execPath),".."):null,f=rF({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=st.join(st.dirname(o),`update-${e}.sh`),p=st.join(st.dirname(o),"last.log");await Hr.writeFile(d,f,{mode:448}),await Hr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let h=NE.existsSync("/run/systemd/system")&&await aF("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${Bt(d)} >> ${Bt(p)} 2>&1`]];try{s(h[0],h[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await Hr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${w?.message||w}`})),{ok:!1,reason:`could not spawn updater: ${w?.message||w}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:d,statusFile:o}}async function sF(){try{return await Hr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function aF(e){for(let t of String(process.env.PATH||"").split(st.delimiter))try{return await Hr.access(st.join(t,e),NE.constants.X_OK),!0}catch{}return!1}var iF=45*6e4;async function BE({statusFile:e=Yu,current:t=zu,now:n=Date.now,staleMs:r=iF}={}){let o=await lc(e);if(!o)return null;let s=a=>Hr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Ju(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function jE({isBusy:e,start:t=Xu,onStuck:n=null,onStarted:r=null,tickMs:o=tF,stuckMs:s=eF,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,f=()=>{u?.timer&&c(u.timer),u=null},d=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let h=u;f();let w=await t({to:h.to,from:h.from,kind:h.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...h,result:w})}catch{}};return{arm({to:p,from:h=zu,kind:w=Lo()}){return u?.to===p?!1:(f(),u={to:p,from:h,kind:w,since:a(),stuckFired:!1,timer:i(d,o)},u.timer.unref?.(),!0)},tick:d,cancel:f,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}me();it();var cF=(...e)=>fetch(...e);function lF(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}async function hw(e=process.env){if(!lF(e))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let t=new AbortController,n=setTimeout(()=>t.abort(),1e4);try{let r=await cF(`${Ie(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function oa(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await hw(t),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function Zu(e,t,{source:n="chat"}={}){return $(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}me();it();var uF=(...e)=>fetch(...e);async function Qu(e,t,n,r=process.env){if(!(Ie(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await uF(`${Ie(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function dF(e,t=process.env){return Qu("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function LE({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await dF(t,o):await oa(e,o),a=await Qu("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function ww(e,t=process.env){let n=await oa(e,t),r=await Qu("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function gw(e,t=process.env){return(await Qu("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function FE(e,t,n,r=process.env){let{blueprint:o,items:s}=await ww(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await $(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}me();it();var fF=(...e)=>fetch(...e);async function pF(e,t,n,r=process.env){if(!(Ie(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await fF(`${Ie(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var UE=72,yw=160;async function HE(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>UE)throw new Error(`the title is the choice as a LABEL \u2014 ${UE} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${yw} chars) a reader can act on without opening the record`);if(c.length>yw)throw new Error(`the summary is one sentence \u2014 ${yw} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await $(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function WE(e,t=process.env){return pF("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}ih();me();import hc from"node:fs/promises";import cd from"node:path";import rU from"node:os";cr();import mF from"node:fs/promises";import qE from"node:path";import hF from"node:os";var KE=()=>qE.join(process.env.BOSTRAT_STATE_DIR||qE.join(process.env.HOME||hF.tmpdir(),".bostrat"),"integrations.json");async function nr(){try{return JSON.parse(await mF.readFile(KE(),"utf8"))||{}}catch{return{}}}var ed=["linked","prOpened","prMerged","create"],bw=["prompt","auto","off"],wF={create:["prompt","off"]},GE=e=>wF[e]??bw;function td(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of ed){let s=GE(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function Fo(){let e=await nr(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:td(e),defaultTeam:n}:null}async function nd(){let e=await nr(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:td(e)}}async function kw(){return td(await nr())}async function VE(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${ed.join(", ")}`);for(let[r,o]of t){if(!ed.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${ed.join(", ")}`);let s=GE(r);if(!s.includes(o))throw new Error(s.length<bw.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${bw.join(", ")}`)}let n=await nr();return n.ticketFlow={...td(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Tt(KE(),n,{mode:384}),n.ticketFlow}async function uc(){let e=(await nr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function gF(){let e=(await nr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var yF={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function rd(){let t=(await nr()).voiceChat||{},n=t.provider||"openai",r=yF[n];if(!r)return null;let o=n==="openai"?await gF():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function zE(){let e=(await nr()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function Uo(){let e=(await nr()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}me();ur();vr();Ks();import fc from"node:fs/promises";import rA from"node:path";import WF from"node:os";import{randomUUID as oA}from"node:crypto";var vw={};Pd(vw,{createComment:()=>_F,findIssueUrl:()=>vF,issueStates:()=>RF,mdToAdf:()=>XE,moveIssue:()=>OF,parseIssueRef:()=>xF,resolveIssue:()=>SF,uploadImage:()=>IF,upsertCard:()=>TF,whoami:()=>xw});var sa=e=>String(e.baseUrl||"").replace(/\/$/,""),JE=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ho(e,t,n,r){let o=await fetch(`${sa(e)}${n}`,{method:t,headers:{authorization:JE(e),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var bF=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,kF=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function xF(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(bF);return r?r[1].toUpperCase():null}let n=t.match(kF);return n?n[1]:null}function vF(e,t=null){let n=null;try{n=t?.baseUrl?new URL(t.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(e||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function SF(e,t){let n;try{n=await Ho(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${sa(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function TF(e,t,{url:n,title:r,subtitle:o}){await Ho(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var EF=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function AF(e){let t=[],n=0;for(let r of e.matchAll(EF)){r.index>n&&t.push({type:"text",text:e.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);t.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?t.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):t.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<e.length&&t.push({type:"text",text:e.slice(n)}),t}function XE(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
659
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...AF(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 _F(e,t,n){let r=await Ho(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:XE(n)});return r?.id?`${sa(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function xw(e){let t=await Ho(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(sa(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function IF(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:JE(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 $F={new:"unstarted",indeterminate:"started",done:"completed"},YE=e=>$F[e?.key]??"started";async function RF(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Ho(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Ho(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:YE(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:YE(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function OF(e,t,n){await Ho(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var Aw={};Pd(Aw,{createComment:()=>LF,createIssue:()=>Ew,createProject:()=>Tw,findIssueUrl:()=>DF,issueStates:()=>UF,moveIssue:()=>HF,parseIssueRef:()=>PF,resolveIssue:()=>BF,teams:()=>aa,uploadImage:()=>FF,upsertCard:()=>jF,whoami:()=>Sw});var CF=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function _n(e,t,n={}){let r=await fetch(CF(),{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 MF=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,NF=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function PF(e){let t=String(e||"").trim(),n=t.match(MF);if(n)return n[1].toUpperCase();let r=t.match(NF);return r?r[1]:null}function DF(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 BF(e,t){let r=(await _n(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 jF(e,t,{url:n,title:r,subtitle:o}){let s=await _n(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 LF(e,t,n){let r=await _n(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 Sw(e){let t=await _n(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 FF(e,{filename:t,contentType:n,buffer:r}){let o=await _n(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
621
660
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
622
661
|
success
|
|
623
662
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
624
663
|
}
|
|
625
|
-
}`,{contentType:n,filename:t,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function YL(e,t){let n=await kn(e,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:t.key});if(!n.issue)throw new Error(`linear: issue "${t.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function XL(e,t,n){if(!(await kn(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Qs(e){return((await kn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var xA=255;async function lw(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>xA,a=s?`${o.slice(0,xA-1).trimEnd()}\u2026`:o,i=await kn(e,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[t],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function uw(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await kn(e,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:t,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var IA={linear:{api:dw,config:Io},jira:{api:iw,config:qu}};async function yw(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(IA)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Oo(e){let t=IA[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function bw(e,t){if(!e||e.name)return e;let n=t.find(r=>r.id===e.id);return n?{...e,name:n.name}:e}var $A=()=>process.env.BOSTRAT_STATE_DIR||EA.join(process.env.HOME||ZL.tmpdir(),".bostrat"),hw=()=>EA.join($A(),"tickets.json"),RA=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Gu=e=>`${RA()}/track/${e}`,Dr=null,fw=null,pw=Promise.resolve();async function ac(){return Dr||(fw??=oc.readFile(hw(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Dr=e,!t&&Me()){let{records:n,source:r}=await Ye("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;Dr[s]=i}Object.keys(Dr).length&&await ww().catch(()=>{})}}return Dr}).finally(()=>fw=null),fw)}async function ww(){let e=JSON.stringify({tickets:Dr},null,2),t=`${hw()}.${process.pid}.tmp`;await oc.mkdir($A(),{recursive:!0}),await oc.writeFile(t,e),await oc.rename(t,hw())}async function Br(e=null){if(pw=pw.then(ww,ww),await pw.catch(()=>{}),!e)return;let t=Dr?.[e];t?ct("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Ft("tickets",e).catch(()=>{})}async function xn(e){return(await ac())[e]??null}async function OA(){return{...await ac()}}async function CA(){return(await yw()).length>0}async function ea(e,t,{source:n="api"}={}){let r=await yw();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(t);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await ac())[e.id]=c,await Br(e.id),await O({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await sc(e.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await rc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await PA(e.id,c,"linked"),c}async function kw(e){let t=await ac();return t[e]?(delete t[e],await Br(e),!0):!1}var gw=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function MA(e,t){if(!e.track?.cwd)return null;let n=wr(await F({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function QL(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${gw(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${gw(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function eF(e,t){let n=[];return e.track.leadAgent&&n.push(`lead ${e.track.leadAgent}`),e.track.branch&&n.push(e.track.branch),e.track.repos?.length&&n.push(e.track.repos.join(" + ")),e.pr?.number&&n.push(`PR #${e.pr.number} ${e.pr.change==="merged"?"merged":"open"}`),t&&n.push(gw(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function sc(e,t){let n=await Oo(t);if(!n)return;let r=await MA(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Gu(e),title:`Bostrat Track: ${t.track.name}`,subtitle:eF(t,r)})}function Vu(e){let t=new URL(RA());return`${t.protocol}//${e}.${t.host}/`}var tF={linked:"linked",opened:"prOpened",merged:"prMerged"};function nF(e,t,n){let r=o=>t.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(e?.type))return r("started")[0]??null}else if(n==="opened"){if(e?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&e?.type!=="completed")return r("completed")[0]??null;return null}async function NA(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Br(e),await O({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function PA(e,t,n){let r=await Oo(t);if(!r)return;let o=r.cfg.ticketFlow?.[tF[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=bw(s,a),c=nF(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??t.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,t,c.id),await NA(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await O({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:_A(),provider:t.provider,key:t.key,url:t.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...t.pr?.number?{prNumber:t.pr.number}:{}}}).catch(()=>{})}async function DA(e){let t=await xn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Oo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=bw(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Br(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function xw(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await xn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Oo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=bw(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Br(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await NA(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function BA(e,t){let n=await xn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Oo(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await O({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function jA(e,t,n){if(!t)throw new Error("offerId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await xw(e.id,o.payload.to?.id,{mode:"offer"})),await O({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var vA=50;async function LA(){let e=await Io();if(!e)throw new Error("no ticketing integration configured");return Qs(e)}function rF(e){let t=e&&typeof e=="object"?e:{},n=(Array.isArray(t.issues)?t.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=t.epic&&String(t.epic.name||"").trim()?{name:String(t.epic.name).trim(),description:t.epic.description?String(t.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>vA)throw new Error(`too many issues (${n.length}); cap is ${vA} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function oF(e,t,n,r){let o=r??await Qs(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await xn(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(e.defaultTeam){let i=s(e.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function FA(e,t){let n=await Io();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=rF(t),o=await Qs(n),s=await oF(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=_A();return await O({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function UA(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await F({id:e.id,cwd:e.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await O({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await Io();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Qs(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await lw(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let w=await uw(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await O({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await O({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&f.length===1&&!await xn(e.id)&&(p=await ea(e,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function rc(e,t){let n=await Oo(e);n&&await n.api.createComment(n.cfg,e,t)}async function sF(e,t,n){let r=await Oo(e);if(!r)return null;let o=await qn(t);if(!o)return null;let s=zt(o,n);if(!s)return null;let a=await oc.readFile(Nt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var SA=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),mw=new Map;function aF(e,t){if(SA()<=0)return sc(e,t);if(mw.has(e))return;let n=setTimeout(()=>{mw.delete(e),xn(e).then(r=>r?sc(e,r):null).catch(()=>{})},SA());n.unref?.(),mw.set(e,n)}var TA=new Map;async function AA(e,t,n){let r=TA.get(e)??0;Date.now()-r<600*1e3||(TA.set(e,Date.now()),await O({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function iF(e,{resolveTrack:t=null}={}){let n=await xn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await yw())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=t?await t(e.trackId):null;if(!s)return;try{await ea(s,o,{source:"chat"})}catch(a){await AA(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Br(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Br(e.trackId),await sc(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Br(e.trackId),await sc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await MA(n,e.trackId).catch(()=>null):null;await rc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
664
|
+
}`,{contentType:n,filename:t,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function UF(e,t){let n=await _n(e,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:t.key});if(!n.issue)throw new Error(`linear: issue "${t.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function HF(e,t,n){if(!(await _n(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function aa(e){return((await _n(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var ZE=255;async function Tw(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>ZE,a=s?`${o.slice(0,ZE-1).trimEnd()}\u2026`:o,i=await _n(e,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[t],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function Ew(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await _n(e,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:t,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var sA={linear:{api:Aw,config:Fo},jira:{api:vw,config:nd}};async function Mw(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(sA)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Wo(e){let t=sA[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Nw(e,t){if(!e||e.name)return e;let n=t.find(r=>r.id===e.id);return n?{...e,name:n.name}:e}var aA=()=>process.env.BOSTRAT_STATE_DIR||rA.join(process.env.HOME||WF.tmpdir(),".bostrat"),Rw=()=>rA.join(aA(),"tickets.json"),iA=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),od=e=>`${iA()}/track/${e}`,Wr=null,_w=null,Iw=Promise.resolve();async function mc(){return Wr||(_w??=fc.readFile(Rw(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(Wr=e,!t&&Ne()){let{records:n,source:r}=await et("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;Wr[s]=i}Object.keys(Wr).length&&await Ow().catch(()=>{})}}return Wr}).finally(()=>_w=null),_w)}async function Ow(){let e=JSON.stringify({tickets:Wr},null,2),t=`${Rw()}.${process.pid}.tmp`;await fc.mkdir(aA(),{recursive:!0}),await fc.writeFile(t,e),await fc.rename(t,Rw())}async function qr(e=null){if(Iw=Iw.then(Ow,Ow),await Iw.catch(()=>{}),!e)return;let t=Wr?.[e];t?wt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Vt("tickets",e).catch(()=>{})}async function In(e){return(await mc())[e]??null}async function cA(){return{...await mc()}}async function lA(){return(await Mw()).length>0}async function ia(e,t,{source:n="api"}={}){let r=await Mw();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let f=u.api.parseIssueRef(t);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await mc())[e.id]=c,await qr(e.id),await $({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await pc(e.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await dc(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await fA(e.id,c,"linked"),c}async function Pw(e){let t=await mc();return t[e]?(delete t[e],await qr(e),!0):!1}var Cw=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function uA(e,t){if(!e.track?.cwd)return null;let n=xr(await F({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function qF(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Cw(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Cw(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function KF(e,t){let n=[];return e.track.leadAgent&&n.push(`lead ${e.track.leadAgent}`),e.track.branch&&n.push(e.track.branch),e.track.repos?.length&&n.push(e.track.repos.join(" + ")),e.pr?.number&&n.push(`PR #${e.pr.number} ${e.pr.change==="merged"?"merged":"open"}`),t&&n.push(Cw(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function pc(e,t){let n=await Wo(t);if(!n)return;let r=await uA(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:od(e),title:`Bostrat Track: ${t.track.name}`,subtitle:KF(t,r)})}function sd(e){let t=new URL(iA());return`${t.protocol}//${e}.${t.host}/`}var GF={linked:"linked",opened:"prOpened",merged:"prMerged"};function VF(e,t,n){let r=o=>t.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(e?.type))return r("started")[0]??null}else if(n==="opened"){if(e?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&e?.type!=="completed")return r("completed")[0]??null;return null}async function dA(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await qr(e),await $({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function fA(e,t,n){let r=await Wo(t);if(!r)return;let o=r.cfg.ticketFlow?.[GF[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Nw(s,a),c=VF(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??t.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,t,c.id),await dA(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await $({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:oA(),provider:t.provider,key:t.key,url:t.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...t.pr?.number?{prNumber:t.pr.number}:{}}}).catch(()=>{})}async function pA(e){let t=await In(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Wo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Nw(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await qr(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function Dw(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await In(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Wo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Nw(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await qr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await dA(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function mA(e,t){let n=await In(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Wo(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await $({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function hA(e,t,n){if(!t)throw new Error("offerId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await Dw(e.id,o.payload.to?.id,{mode:"offer"})),await $({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var QE=50;async function wA(){let e=await Fo();if(!e)throw new Error("no ticketing integration configured");return aa(e)}function zF(e){let t=e&&typeof e=="object"?e:{},n=(Array.isArray(t.issues)?t.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=t.epic&&String(t.epic.name||"").trim()?{name:String(t.epic.name).trim(),description:t.epic.description?String(t.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>QE)throw new Error(`too many issues (${n.length}); cap is ${QE} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function YF(e,t,n,r){let o=r??await aa(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await In(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(e.defaultTeam){let i=s(e.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function gA(e,t){let n=await Fo();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=zF(t),o=await aa(n),s=await YF(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=oA();return await $({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function yA(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await F({id:e.id,cwd:e.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await Fo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await aa(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let h=await Tw(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await Ew(a,{teamId:c.id,title:h.title,description:h.description,projectId:f,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await $({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await $({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(h=>h.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await In(e.id)&&(p=await ia(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function dc(e,t){let n=await Wo(e);n&&await n.api.createComment(n.cfg,e,t)}async function JF(e,t,n){let r=await Wo(e);if(!r)return null;let o=await Yn(t);if(!o)return null;let s=tn(o,n);if(!s)return null;let a=await fc.readFile(Wt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var eA=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),$w=new Map;function XF(e,t){if(eA()<=0)return pc(e,t);if($w.has(e))return;let n=setTimeout(()=>{$w.delete(e),In(e).then(r=>r?pc(e,r):null).catch(()=>{})},eA());n.unref?.(),$w.set(e,n)}var tA=new Map;async function nA(e,t,n){let r=tA.get(e)??0;Date.now()-r<600*1e3||(tA.set(e,Date.now()),await $({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function ZF(e,{resolveTrack:t=null}={}){let n=await In(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Mw())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=t?await t(e.trackId):null;if(!s)return;try{await ia(s,o,{source:"chat"})}catch(a){await nA(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await qr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await qr(e.trackId),await pc(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await qr(e.trackId),await pc(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await uA(n,e.trackId).catch(()=>null):null;await dc(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
626
665
|
|
|
627
|
-
Agent cost for this Track: ${
|
|
666
|
+
Agent cost for this Track: ${qF(o)}.`:"")),await fA(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await JF(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await dc(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
628
667
|
|
|
629
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
630
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
631
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
668
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await dc(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 dc(n,`**Bostrat** \u2014 live preview **${r.name}**: ${sd(r.slug)}
|
|
669
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await XF(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await nA(e.trackId,s?.cwd??null,o)}}function bA({resolveTrack:e}){ct.on("event",t=>{ZF(t,{resolveTrack:e}).catch(()=>{})})}async function kA(){if(!Ne())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await et("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await mc(),r=0;for(let[o,s]of Object.entries(n))await wt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}bo();Ks();import QF from"ws";var xA=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function ad(e,t,n={}){let r=await fetch(`${xA()}/${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 Bw=(e,t)=>ad("chat.postMessage",e,t),qo=(e,t)=>ad("chat.update",e,t);async function bt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function eU(e,t,n={}){let r=await fetch(`${xA()}/${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 vA(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await eU("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 ad("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var tU=2200;function SA({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 ad("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new QF(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},p=!1,h=setTimeout(()=>{p=!0,d()},tU);Promise.resolve().then(()=>t(f)).then(w=>{clearTimeout(h),!p&&(p=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(h),p||(p=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var AA=()=>process.env.BOSTRAT_STATE_DIR||cd.join(process.env.HOME||rU.tmpdir(),".bostrat"),Hw=()=>cd.join(AA(),"slack.json"),id=null,jw=null,Lw=Promise.resolve();async function qw(){return id||(jw??=hc.readFile(Hw(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>id=e).finally(()=>jw=null),jw)}async function TA(){let e=JSON.stringify({threads:id},null,2),t=`${Hw()}.${process.pid}.tmp`;await hc.mkdir(AA(),{recursive:!0}),await hc.writeFile(t,e),await hc.rename(t,Hw())}async function sn(){Lw=Lw.then(TA,TA),await Lw.catch(()=>{})}async function Kw(e){return(await qw())[e]??null}async function oU(e,t){let n=await qw();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var ke=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function wc(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function sU(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return wc(n.replace(/\n+/g," "),t)}function _A(e){let t=`*${ke(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${ke(e.repoLabel)} \xB7 \`${ke(e.branch||"?")}\` \xB7 lead *${ke(e.lead)}*`,r=e.kickoff?`> ${ke(wc(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
670
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${od(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Ww(e,t){await qo(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:_A(t)})}async function Gr(e,t,n,r=void 0){return Bw(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var EA=new Set;function Kr(e){EA.add(e),e.catch(()=>{}).finally(()=>EA.delete(e))}var Me=e=>({response_type:"ephemeral",text:e}),aU="*/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._",iU=["claude","cursor","codex"];function Fw(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 Uw=(e,t)=>Me(`No live Track matches \u201C${ke(t)}\u201D. Live: ${e.map(n=>`\`${ke(n.name)}\``).join(" \xB7 ")||"none"}`);async function IA(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=t.maxConcurrent){await bt(o,{...Me(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await bt(o,{...Me("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?cd.basename(c.base):"fresh workspace"),lead:"claude"},u=await Bw(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:_A(l),unfurl_links:!1});l.ts=u.ts,(await qw())[i.id]=l,await sn(),await bt(o,{...Me(`\u2713 *${ke(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await bt(o,{...Me(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function cU(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Kw(n.id);s&&(s.archivedAt=Date.now(),await sn(),await Ww(t,s).catch(()=>{})),await bt(r,{...Me(`\u2713 *${ke(n.name)}* archived.`),replace_original:!0})}catch(s){await bt(r,{...Me(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function lU(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await bt(o,{...Me(`\u2713 Handing *${ke(n.name)}* to ${ke(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await bt(o,{...Me(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function uU(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await bt(r,{...Me(`\u2713 Stopped *${ke(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await bt(r,{...Me(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function dU(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:wc(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:e}),text:{type:"mrkdwn",text:`Repo for *${ke(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function fU(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?cd.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${ke(s.name)}* \u2014 ${ke(i)}${a.branch?` \`${ke(a.branch)}\``:""} \xB7 lead ${ke(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
632
671
|
`)+(o?`
|
|
633
|
-
_${o} archived_`:"")}async function
|
|
634
|
-
\u2713 *${
|
|
635
|
-
\u2713 _answered_`}}]})}async function
|
|
636
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
637
|
-
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function HF(e){let t=new Set;for(let n of e)if(n.type==="cost.recorded"){n.payload?.messageId&&t.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])t.add(r)}return t}var WF=new Set(["Write","Edit","NotebookEdit"]),qF=220,KF=160;function GF(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(WF.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=t?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,qF)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Jr(o.command.trim().replace(/\s+/g," ")).slice(0,KF));return r}function VF(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Wt(t.cwd,t.sessionId):t?.cwd?af(t.cwd):null}async function QA(e,t){let n=await t(e?.session_id,e?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await O(r,{...s,...c}))};switch(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=Zl(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:GF(e,o)});break;case"Stop":await zF(r,o,e,i);break;default:break}return a}async function zF(e,t,n,r){let o=[],s=VF(n,t);if(s)try{o=UF(await FF.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??t?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=HF(await F(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=`${d.model||"unknown"}\0${d.speed||""}`,p=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=d.tokens[m];d.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=d.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=d.cacheWriteTtl.h1),p.messageIds.push(d.messageId),p.speed=d.speed||null,u.set(f,p)}for(let[d,f]of u){let p=d.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:an(p,f.tokens,void 0,f.speed?{speed:f.speed}:void 0),tokens:f.tokens,...f.speed?{speed:f.speed}:{},cacheWriteTtl:f.cacheWriteTtl,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}Lo();Zd();ue();fl();su();import{execFile as JF}from"node:child_process";import{promisify as YF}from"node:util";var XF=YF(JF),Ow=(e,t)=>XF("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4});function eE(e,t=Date.now()){return Bd(e,t)??0}function ZF(e,t,n){for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="cost.recorded"||o.boxId!==t||n!=null&&o.epoch!=null&&o.epoch!==n)continue;let s=o.payload?.model;if(s&&s!=="unknown")return s}return null}async function tE(e,t,{now:n=Date.now()}={}){let r=eE(t,n),o=await F(e);return ou({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:ZF(o,t.name,r)})}function QF(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=null;for(let c=n.length-1;c>=0;c--){let l=n[c];if(l.type==="turn.user"&&l.boxId===t.name&&l.actor==="user"){i=Rn(l.payload?.promptHead||l.payload?.text||"")?.category??null;break}}return{wallReason:a,fixerCategory:i}}async function eU(e,t){try{let{stdout:n}=await Ow(e,["show","--shortstat","--format=",t]),r=/(\d+) files? changed/.exec(n),o=/(\d+) insertions?\(\+\)/.exec(n),s=/(\d+) deletions?\(-\)/.exec(n);return{files:r?Number(r[1]):0,insertions:o?Number(o[1]):0,deletions:s?Number(s[1]):0}}catch{return{files:0,insertions:0,deletions:0}}}async function tU(e){try{let{stdout:t}=await Ow(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function nE(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await F(e);if(a.some(p=>p.type==="code.committed"&&p.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:p}=await Ow(r,["show","-s","--format=%ct",n]),m=Number(p.trim())*1e3;Number.isFinite(m)&&m>0&&(i=m)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=eE(t,i),l=await eU(r,n),u=await O(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:await tU(r),...l,ts:i}}),d=null,f=!1;try{let p=await Dv(r,n,{fixKey:Ns({trackId:e.id,boxId:t.name,epoch:c}),window:$r,context:{...QF(e,t,a,c),provider:t.agent??null}});if(p){d=await O(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:u.id,payload:p});let m=Bv(p);if(m>=Pv){let w=p.origins.filter(y=>y.class==="rework").slice(0,3);await O(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:{sha:n,subject:p.subject,linesReworked:m,origins:w.map(y=>({key:y.key,provider:y.provider,model:y.model,lines:y.lines,ageDays:y.ageDays})),heuristic:p.heuristic,ts:i}}),f=!0}}}catch(p){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${p?.message||p}`)}return{recorded:!0,event:u,rework:d,offered:f}}async function rE(e,t,n){if(!xi.includes(n))throw new Error(`bad cause "${n}" (${xi.join("|")})`);let r=await F(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(r.some(s=>s.type==="rework.cause.answered"&&s.payload?.sha===t))throw new Error("already answered");return O(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,cause:n,level:"B",ts:Date.now()}})}ue();ue();gr();ue();gr();On();function lc(e){let t={};for(let n of e.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";t[n.name]=`${n.name} (${n.agent||"?"}${r})`}return t}var oE=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),sE=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Xu=e=>Math.round(e*1e4)/1e4;async function Cw(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=oE(),d=0,f=[];for(let p of e||[]){let m=await F(p),w=wr(m,{boxLabels:lc(p)});if(w.turns===0){p.archivedAt?d++:f.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);sE(u,w.totalTokens);for(let[y,x]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:oE()},n[y].usd=Xu(n[y].usd+x.usd),n[y].events+=x.events,sE(n[y].tokens,x.tokens);for(let[y,x]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=Xu(r[y].usd+x.usd),r[y].events+=x.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:ls(),fleet:{totalUsd:Xu(o),asRecordedUsd:Xu(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:d+f.length,tracksSilentArchived:d,tracksSilentLive:f},tracks:t}}var Fr=e=>`$${e.toFixed(4)}`,ta=(e,t)=>String(e).padEnd(t);function aE(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];if(r.push(`fleet total ${Fr(t.totalUsd)} over ${t.turns} turn(s) \xB7 ${t.tracksWithCost} track(s) with cost, ${t.tracksSilentArchived??t.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),t.repricedTurns&&r.push(`${t.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),t.asRecordedUsd!=null&&Math.abs(t.asRecordedUsd-t.totalUsd)>=.005&&r.push(`as-recorded total ${Fr(t.asRecordedUsd)} \xB7 current-rates total ${Fr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Fr(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${ta(a,26)} ${ta(Fr(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${ta(a.name||a.id,30)} ${Fr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ta(c.label,40)} ${ta(Fr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${ta(Fr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
638
|
-
`)}ue();Np();import aU from"node:fs/promises";import iU from"node:path";fl();gr();var Zu=e=>e.payload?.ts??e.ts,nU=12e4,Ur=e=>Math.round(e*1e4)/1e4,rU=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function oU(e,{since:t}){let{track:n,events:r,brief:o}=e,s=T=>Zu(T)>=t,a=Rn([Xa(n?.name),Xa(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let T of r)if(T.type==="subtask.delegated"&&T.boxId&&T.payload?.task){let I=Rn(T.payload.task);I&&i.set(T.boxId,I)}let c=new Map,l=T=>{let I=T||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=T=>({textCat:T,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((T,I)=>Zu(T)-Zu(I)),f=0,p=0,m=0,w={turns:0};for(let T of d){let I=l(T.boxId),$=I.segments[I.segments.length-1],D=Zu(T);switch(I.lastTs!=null&&D>I.lastTs&&s(T)&&($.activeMs+=Math.min(D-I.lastTs,nU)),I.lastTs=D,T.provider&&(I.provider=T.provider),T.type){case"turn.user":{let S=T.payload?.text;if(T.source==="hook"||!String(S||"").trim())break;let A=u(Rn(S));I.segments.push(A),s(T)&&(A.turns+=1);break}case"peer.message":{let S=T.payload?.text;if(!String(S||"").trim())break;let A=u(Rn(S));I.segments.push(A),s(T)&&(A.turns+=1);break}case"tool.called":{if(T.source==="transcript")break;let S=T.payload?.tool;S&&($.tools[S]=($.tools[S]||0)+1),s(T)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(T))break;let{usd:S,tokens:A,model:R,unpriced:E}=Ot(T);E&&(w.turns+=1),$.usd+=S,A&&($.tokens.input+=A.input,$.tokens.output+=A.output,$.tokens.cacheRead+=A.cacheRead,$.tokens.cacheWrite+=A.cacheWrite),$.models[R]=($.models[R]||0)+S,T.provider&&($.provider=T.provider);break}case"handoff.done":s(T)&&(f+=1);break;case"subtask.merged":s(T)&&(p+=1);break;case"error":s(T)&&T.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,x=!1;for(let[T,I]of c)for(let $ of I.segments){if(!($.usd>0||$.activeMs>0||$.turns>0||$.toolCalls>0))continue;let S=$.textCat?.category??i.get(T)?.category??Ab($.tools)?.category??a?.category??"uncategorized";y.has(S)||y.set(S,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let A=y.get(S);A.usd+=$.usd,A.activeMs+=$.activeMs,A.turns+=$.turns,A.toolCalls+=$.toolCalls;for(let[E,L]of Object.entries($.models))A.models[E]=(A.models[E]||0)+L;let R=$.provider||I.provider;R&&(A.providers[R]=(A.providers[R]||0)+$.usd),R==="cursor"&&$.usd===0&&(x=!0)}return{perCategory:y,handoffs:f,merged:p,failed:m,sawCursorActivity:x,unpricedTurns:w.turns}}function cE(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let w of e||[]){let{track:y,events:x}=w;if(!x?.length){a+=1;continue}let{perCategory:T,handoffs:I,merged:$,failed:D,sawCursorActivity:S,unpricedTurns:A}=oU(w,{since:r});S&&(i=!0),c+=A;let R={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:I,subtasksMerged:$,subtasksFailed:D};for(let[E,L]of T){R.usd+=L.usd,R.activeMs+=L.activeMs,R.turns+=L.turns,R.categories[E]={usd:Ur(L.usd),activeMs:L.activeMs};for(let[G,C]of Object.entries(L.models))R.models[G]=Ur((R.models[G]||0)+C);for(let[G,C]of Object.entries(L.providers))R.providers[G]=Ur((R.providers[G]||0)+C);o.has(E)||o.set(E,rU());let U=o.get(E);U.usd+=L.usd,U.activeMs+=L.activeMs,U.turns+=L.turns,U.toolCalls+=L.toolCalls;for(let[G,C]of Object.entries(L.models))U.models[G]=(U.models[G]||0)+C;for(let[G,C]of Object.entries(L.providers))U.providers[G]=(U.providers[G]||0)+C;let se=U.byTrack.get(y?.id)||{usd:0,activeMs:0};se.usd+=L.usd,se.activeMs+=L.activeMs,U.byTrack.set(y?.id,se)}(R.usd>0||R.activeMs>0||R.turns>0)&&(R.usd=Ur(R.usd),R.dominantCategory=sU(R.categories),s.push(R))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,p=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:f>0?Math.round((d==="usd"?y.usd:y.activeMs)/f*100):0,shareBasis:d,usd:Ur(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:iE(y.models),providers:iE(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((x,T)=>T[1].usd-x[1].usd||T[1].activeMs-x[1].activeMs).slice(0,2).map(([x,T])=>({trackId:x,name:p.get(x),usd:Ur(T.usd),activeMs:T.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:Ur(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var sU=e=>{let t=null;for(let[n,r]of Object.entries(e))(!t||r.usd>t.v.usd||r.usd===t.v.usd&&r.activeMs>t.v.activeMs)&&(t={cat:n,v:r});return t?.cat??null},iE=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ur(n)]));var na={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function cU(e){let t=String(e||"7d");return t in na?{key:t,ms:na[t]}:{key:"7d",ms:na["7d"]}}function lU(e,t){let n=new Set((t||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...t||[]];for(let o of e?.events||[]){let s=Pl(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:e.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function uU(e){if(!e?.cwd)return null;try{let n=(await aU.readFile(iU.join(e.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function lE(e,{window:t="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:cU(t),d=[];for(let f of e||[]){let p=await F(f),m=lU(f,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),d.push({track:f,events:m,brief:await uU(f)})}return{window:l,...cE(d,{windowMs:u,now:c})}}On();var Hr=36e5,pE=24*Hr,dU=400,fU=12,Mo=e=>Math.round(e*1e4)/1e4;function Mw({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in na?String(e||"7d"):"7d",i=na[a];return{key:a,since:i==null?null:r-i,until:r}}var pU=(e,t)=>e!=null&&t-e<=48*Hr?Hr:pE;function uE(e,t){if(t===Hr)return Math.floor(e/Hr)*Hr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function mU(e,t){if(t===Hr)return e+Hr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var dE=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),fE=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},hU=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Mo(n)]));async function mE(e,t={}){let n=t.now??Date.now(),r=Mw({...t,now:n}),o=r.since==null?pE:pU(r.since,r.until),s=E=>(r.since==null||E>=r.since)&&E<r.until,a=r.since!=null?r.until-r.since:null,i=E=>a!=null&&E>=r.since-a&&E<r.since,c={},l={},u=dE(),d=0,f=0,p=0,m=0,w=0,y=0,x=new Set,T=new Map,I=[],$=[],D=[],S=null;for(let E of e||[]){let U=(await F(E)).filter(C=>C.type==="cost.recorded");if(!U.length){E.archivedAt||D.push(E.name||E.id);continue}let se=[];for(let C of U){let Z=C.ts??0;s(Z)?se.push(C):i(Z)&&(y+=Ot(C).usd)}if(!se.length)continue;let G=wr(se,{boxLabels:lc(E)});d+=G.totalUsd,f+=G.turns,p+=G.repricedTurns||0,m+=G.asRecordedUsd||0,w+=G.unpricedTurns||0;for(let C of G.unpricedModels||[])x.add(C);fE(u,G.totalTokens);for(let[C,Z]of Object.entries(G.byModel))c[C]||={usd:0,events:0,tokens:dE()},c[C].usd=Mo(c[C].usd+Z.usd),c[C].events+=Z.events,fE(c[C].tokens,Z.tokens);for(let[C,Z]of Object.entries(G.byProvider))l[C]||={usd:0,events:0},l[C].usd=Mo(l[C].usd+Z.usd),l[C].events+=Z.events;for(let C of se){let{usd:Z}=Ot(C);if(!Z)continue;let Y=uE(C.ts??0,o),te=T.get(Y)??{t:Y,total:0,byProvider:{}};te.total+=Z;let we=C.provider||"unknown";te.byProvider[we]=(te.byProvider[we]||0)+Z,T.set(Y,te),(S==null||Y<S)&&(S=Y)}I.push({id:E.id,name:E.name||E.id,archived:!!E.archivedAt,usd:G.totalUsd,turns:G.turns,providers:Object.keys(G.byProvider)});for(let[C,Z]of Object.entries(G.byBox))$.push({trackId:E.id,trackName:E.name||E.id,boxId:C,...Z})}let A=r.since!=null?uE(r.since,o):S,R=[];for(let E=A,L=0;E!=null&&E<r.until&&L<dU;E=mU(E,o),L++){let U=T.get(E);R.push(U?{t:E,total:Mo(U.total),byProvider:hU(U.byProvider)}:{t:E,total:0,byProvider:{}})}return I.sort((E,L)=>L.usd-E.usd),$.sort((E,L)=>L.usd-E.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:ls(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:R,fleet:{totalUsd:Mo(d),asRecordedUsd:Mo(m),turns:f,repricedTurns:p,unpricedTurns:w,unpricedModels:[...x],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:I.length,tracksSilentLive:D},prior:a!=null?{totalUsd:Mo(y),since:r.since-a,until:r.since}:null,tracks:I.slice(0,50),boxes:$.slice(0,fU)}}su();var wU=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),gU=20,yU=40;async function hE(e,t={}){let n=t.now??Date.now(),r=Mw({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await F(l),d=0,f=lc(l);for(let p of u){if(!wU.has(p.type))continue;let m=p.type==="code.rework"?p.payload?.ts??p.ts:p.ts;p.type!=="rework.cause.answered"&&!o(m??0)||(p.type==="code.committed"&&d++,s.push(p.trackId?p:{...p,trackId:l.id}))}d&&i++;for(let p of l.boxes||[])a[`${l.id}/${p.name}`]={trackName:l.name||l.id,label:f[p.name]||p.name}}let c=jv(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:$r,decayEndDays:mm,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,gU),fixes:c.fixes.slice(0,yU)}}import bU from"node:fs";import wE from"node:fs/promises";import uc from"node:os";import kU from"node:path";Om();nt();var xU=1024*1024,Qu=e=>Math.round(e/xU);async function vU(e){try{let t=await wE.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Qu(r),diskTotalMb:Qu(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function SU(){try{return(await Ut()).filter(t=>t.isBostrat).length}catch{return null}}async function TU(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await wE.readdir(kU.join(Xi,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function gE({stateDir:e=Xi,now:t=Date.now}={}){let n=uc.totalmem(),[r,o,s,a]=await Promise.all([fu().catch(()=>null),vU(bU.existsSync(e)?e:uc.tmpdir()),SU(),TU()]),i=uc.cpus()?.length||null,c=uc.loadavg()?.[0]??null;return{ts:t(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?Qu(n-r):null,memTotalMb:Qu(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(uc.uptime())}}ue();Cf();Aa();On();ue();On();var AU=864e5;function EU(e,{tolerance:t=.005}={}){if(e.type!=="cost.recorded"||e.source==="vendor")return null;let n=e.payload?.tokens,r=Number(e.payload?.usd)||0;if(!(n?(n.input||0)+(n.output||0)+(n.cacheRead||0)+(n.cacheWrite??n.cacheCreation??0):0)||!r)return null;let s=e.payload?.model||"unknown",a=e.payload?.speed||void 0,i={input:n.input||0,output:n.output||0,cacheRead:n.cacheRead||0,cacheWrite:n.cacheWrite??n.cacheCreation??0},c=an(s,i,e.ts,a?{speed:a}:void 0);if(!c)return null;let l=r-c,u=Math.abs(l)>Math.max(c*t,.001);return{model:s,speed:a??null,ts:e.ts,asRecorded:r,expected:c,delta:l,divergent:u}}async function yE(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*AU,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await F(u);for(let f of d){if(!f.ts||f.ts<o)continue;let p=EU(f,{tolerance:n});if(!p)continue;a++;let m=p.speed?`${p.model} (${p.speed})`:p.model,w=s[m]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};w.rows++,w.asRecordedUsd+=p.asRecorded,w.expectedUsd+=p.expected,p.divergent&&(i++,w.divergent++,c.length<10&&c.push({track:u.name||u.id,...p}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}um();Cl();it();Cn();import ra from"node:fs/promises";import _U from"node:fs";import ot from"node:path";import IU from"node:os";import{execFile as $U}from"node:child_process";import{promisify as RU}from"node:util";var OU=RU($U),bE=process.env.BOSTRAT_STATE_DIR||ot.join(process.env.HOME||IU.tmpdir(),".bostrat"),ed=!1,Nw=null;async function Pw(e,t){let n=await ra.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ra.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ra.mkdir(ot.dirname(t),{recursive:!0}),await ra.copyFile(e,t),!0)}var CU=new Set([".claude",".codex",".cursor",".credentials.json","auth.json",".claude.json","api-token","node.env","certs","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function td(e,t,{skip:n=null}={}){let r=0,o=await ra.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(CU.has(s.name)||n?.has(s.name))continue;let a=ot.join(e,s.name);s.isDirectory()?r+=await td(a,ot.join(t,s.name)):s.isFile()&&(r+=await Pw(a,ot.join(t,s.name))?1:0)}return r}async function kE({stateDir:e=bE,dest:t=process.env.BOSTRAT_SYNC_DIR||`${bE}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ra.mkdir(t,{recursive:!0});let o=0;o+=await Pw(ot.join(e,"tracks.json"),ot.join(t,"tracks.json"))?1:0,o+=await Pw(ot.join(e,"events.jsonl"),ot.join(t,"events.jsonl"))?1:0,o+=await td(ot.join(e,"events"),ot.join(t,"events")),o+=await td(ot.join(e,"memory"),ot.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await td(ot.join(u.cwd,".bostrat"),ot.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>OU("git",["-C",t,...u]);_U.existsSync(ot.join(t,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Md(t).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||ed))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,ed&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),ed=!1,Nw=null}catch(u){ed=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
639
|
-
`).slice(-3).join(" | ").slice(0,300);
|
|
640
|
-
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,t.length)&&!/^Current\s/i.test(t[c]);c++){let l=t[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=t[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=t.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return t.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function
|
|
641
|
-
`).map(s=>s.replace(/\s+$/,"")),n=t.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<t.length&&!r.test(t[s]);s++)t[s].trim()&&o.push(t[s].trim());return o.length?o:null}async function
|
|
642
|
-
`),r=!1;for(let o=n.length-1;o>=0;o--){let s=n[o].trim();if(!s||s[0]!=="{"||!s.includes("token_count"))continue;let a;try{a=JSON.parse(s)}catch{continue}let i=a?.payload;if(i?.type!=="token_count")continue;r=!0;let c=i.rate_limits;if(!c)return
|
|
643
|
-
`,{mode:384}),
|
|
672
|
+
_${o} archived_`:"")}async function pU(e,t){let n=await Uo();if(!n)return Me("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 Me("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(f=>f.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(Kr(IA(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Me(`Starting *${ke(u)}* on ${ke(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:dU(u,i)}}case"archive":{if(!a.length)return Me("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Fw(await r.listTracks(),a.join(" "));return i?(Kr(cU(e,n,{track:i,responseUrl:t.response_url})),Me(`Archiving *${ke(i.name)}*\u2026`)):Uw(c,a.join(" "))}case"handoff":{let i=r.knownAgents??iU,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Me(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=Fw(await r.listTracks(),l);return u?(Kr(lU(e,n,{track:u,agent:c,responseUrl:t.response_url})),Me(`Handing *${ke(u.name)}* to ${ke(c)}\u2026`)):Uw(f,l)}case"stop":{if(!a.length)return Me("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Fw(await r.listTracks(),a.join(" "));return i?(Kr(uU(e,n,{track:i,responseUrl:t.response_url})),Me(`Stopping *${ke(i.name)}*\u2026`)):Uw(c,a.join(" "))}case"list":return Me(await fU(r));default:return Me(aU)}}async function mU(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Kw(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 sn(),await qo(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ke(l||"question")}~
|
|
673
|
+
\u2713 *${ke(a)}*`}}]}).catch(()=>{})}}catch(c){await bt(n.response_url,Me(`Couldn't answer: ${c?.message||c}`))}}async function hU(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(d=>d.type==="pr.offered"&&d.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===s))throw new Error("offer already answered");let f=Array.isArray(r.actions)?Ar.filter(d=>r.actions.includes(d)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(f?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:f}))return;f&&await i.sayToLead(c.id,Os(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:f?a:"dismiss",actions:f}})}catch(c){await bt(n.response_url,Me(`Couldn't act on the offer: ${c?.message||c}`))}}async function wU(e,t,n,r){let{trackId:o,offerId:s,choice:a}=r;try{let i=await e.deps.resolveTrack(o);if(!i)throw new Error("track not found");await e.deps.answerPrExists(i,{offerId:s,choice:a})}catch(i){await bt(n.response_url,Me(`Couldn't act on the offer: ${i?.message||i}`))}}async function gU(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Uo();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await bt(t.response_url,{...Me(`Starting *${ke(s)}*\u2026`),replace_original:!0}),Kr(IA(e,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:t.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?Kr(mU(e,r,t,o)):n.action_id==="bostrat-ship"?Kr(hU(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&Kr(wU(e,r,t,o))}async function yU(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 oU(t.channel,t.thread_ts);if(!n)return;let r=await Uo();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Gr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function bU(e){return async t=>{if(t.type==="slash_commands")return pU(e,t.payload);if(t.type==="interactive"){await gU(e,t.payload);return}if(t.type==="events_api"){await yU(e,t.payload?.event);return}}}var kU=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 xU(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${ke(t.question||"The lead needs an answer")}`}}],r=(t.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:wc(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 vU(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await qo(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ke(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Gr(e,t,r.question||"The lead needs an answer",xU(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await sn()}async function SU(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 sn(),await qo(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ke(s||"question")}~
|
|
674
|
+
\u2713 _answered_`}}]})}async function TU(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(f=>l[f]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await Gr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${ke(wc(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await sn()}async function EU(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await sn();let s=r.action==="dismiss"?"dismissed":r.action==="pr-exists"?`PR${r.existingPr?.number?` #${r.existingPr.number}`:""} already open \u2014 pick how to ship`:r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await qo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function AU(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=r.pr?.number?`#${r.pr.number}`:"a PR",s=(i,c,l)=>({type:"button",action_id:"bostrat-pr-exists",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,choice:i}),...l?{style:l}:{}}),a=await Gr(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${ke(r.branch)})`:""} is already open. Push the update to it, or open a new PR?`}},{type:"actions",elements:[s("push",`Push to ${o}`,"primary"),s("new","Open a new PR"),s("dismiss","Not now")]}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await sn()}async function _U(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await sn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await qo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function IU(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??$U)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await vA(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${ke(o.name)}*${a}${o.group?` (group \`${ke(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${ke(a.name)}*`).join(", ");await Gr(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${ke(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function $U(e,t){let n=await Yn(e);if(!n)return null;let r=tn(n,t);if(!r)return null;let o=await hc.readFile(Wt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function RU(e,t){if(!kU.has(t.type))return;let n=await Kw(t.trackId);if(!n)return;let r=await Uo();if(!r)return;let o=t.payload||{};switch(t.type){case"turn.user":if(n.kickoff||t.actor!=="user"||!o.text)return;n.kickoff=o.text,await sn(),await Ww(r,n);return;case"turn.agent":{if(!o.text)return;let s=sU(o.text);if(!s)return;await Gr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:ke(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${od(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return vU(r,n,t);case"prompt.answered":return SU(r,n,t);case"pr.offered":return TU(r,n,t);case"pr.offer.answered":return EU(r,n,t);case"pr.exists.offered":return AU(r,n,t);case"pr.exists.answered":return _U(r,n,t);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await Gr(r,n,`${s}${o.url?`: ${o.url}`:o.number?` (#${o.number})`:""}`);return}case"handoff.done":if(!o.toAgent||o.toAgent===n.lead)return;n.lead=o.toAgent,await sn(),await Ww(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Gr(r,n,`Live preview ${o.name}: ${sd(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${ke(o.name)}*: ${sd(o.slug)}
|
|
675
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return IU(r,n,t,e.deps)}}function $A(e){let t={deps:e};ct.on("event",s=>{RU(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Uo()||(r&&clearInterval(r),r=null,n=SA({getToken:async()=>(await Uo())?.appToken,onEnvelope:bU(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}Xt();me();dn();um();Dn();import OU from"node:fs/promises";function CU(e){let t=new Map;for(let n of String(e||"").split(`
|
|
676
|
+
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function MU(e){let t=new Set;for(let n of e)if(n.type==="cost.recorded"){n.payload?.messageId&&t.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])t.add(r)}return t}var NU=new Set(["Write","Edit","NotebookEdit"]),PU=220,DU=160;function BU(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(NU.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=t?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,PU)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=no(o.command.trim().replace(/\s+/g," ")).slice(0,DU));return r}function jU(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Jt(t.cwd,t.sessionId):t?.cwd?gf(t.cwd):null}async function RA(e,t){let n=await t(e?.session_id,e?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await $(r,{...s,...c}))};switch(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=iu(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:BU(e,o)});break;case"Stop":await LU(r,o,e,i);break;default:break}return a}async function LU(e,t,n,r){let o=[],s=jU(n,t);if(s)try{o=CU(await OU.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??t?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=MU(await F(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=`${f.model||"unknown"}\0${f.speed||""}`,p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let h of Object.keys(p.tokens))p.tokens[h]+=f.tokens[h];f.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=f.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=f.cacheWriteTtl.h1),p.messageIds.push(f.messageId),p.speed=f.speed||null,u.set(d,p)}for(let[f,d]of u){let p=f.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:mn(p,d.tokens,void 0,d.speed?{speed:d.speed}:void 0),tokens:d.tokens,...d.speed?{speed:d.speed}:{},cacheWriteTtl:d.cacheWriteTtl,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.messageIds.length}})}}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}Yo();lf();me();kl();wu();import{execFile as FU}from"node:child_process";import{promisify as UU}from"node:util";var HU=UU(FU),Gw=(e,t)=>HU("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4});function OA(e,t=Date.now()){return Vd(e,t)??0}function WU(e,t,n){for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="cost.recorded"||o.boxId!==t||n!=null&&o.epoch!=null&&o.epoch!==n)continue;let s=o.payload?.model;if(s&&s!=="unknown")return s}return null}async function CA(e,t,{now:n=Date.now()}={}){let r=OA(t,n),o=await F(e);return hu({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:WU(o,t.name,r)})}function qU(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=null;for(let c=n.length-1;c>=0;c--){let l=n[c];if(l.type==="turn.user"&&l.boxId===t.name&&l.actor==="user"){i=Pn(l.payload?.promptHead||l.payload?.text||"")?.category??null;break}}return{wallReason:a,fixerCategory:i}}async function KU(e,t){try{let{stdout:n}=await Gw(e,["show","--shortstat","--format=",t]),r=/(\d+) files? changed/.exec(n),o=/(\d+) insertions?\(\+\)/.exec(n),s=/(\d+) deletions?\(-\)/.exec(n);return{files:r?Number(r[1]):0,insertions:o?Number(o[1]):0,deletions:s?Number(s[1]):0}}catch{return{files:0,insertions:0,deletions:0}}}async function GU(e){try{let{stdout:t}=await Gw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function MA(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await F(e);if(a.some(p=>p.type==="code.committed"&&p.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:p}=await Gw(r,["show","-s","--format=%ct",n]),h=Number(p.trim())*1e3;Number.isFinite(h)&&h>0&&(i=h)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=OA(t,i),l=await KU(r,n),u=await $(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:await GU(r),...l,ts:i}}),f=null,d=!1;try{let p=await mS(r,n,{fixKey:Us({trackId:e.id,boxId:t.name,epoch:c}),window:Dr,context:{...qU(e,t,a,c),provider:t.agent??null}});if(p){f=await $(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:u.id,payload:p});let h=hS(p);if(h>=pS){let w=p.origins.filter(g=>g.class==="rework").slice(0,3);await $(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:f.id,payload:{sha:n,subject:p.subject,linesReworked:h,origins:w.map(g=>({key:g.key,provider:g.provider,model:g.model,lines:g.lines,ageDays:g.ageDays})),heuristic:p.heuristic,ts:i}}),d=!0}}}catch(p){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${p?.message||p}`)}return{recorded:!0,event:u,rework:f,offered:d}}async function NA(e,t,n){if(!$i.includes(n))throw new Error(`bad cause "${n}" (${$i.join("|")})`);let r=await F(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(r.some(s=>s.type==="rework.cause.answered"&&s.payload?.sha===t))throw new Error("already answered");return $(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,cause:n,level:"B",ts:Date.now()}})}me();me();vr();me();vr();Dn();function gc(e){let t={};for(let n of e.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";t[n.name]=`${n.name} (${n.agent||"?"}${r})`}return t}var PA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),DA=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},ld=e=>Math.round(e*1e4)/1e4;async function Vw(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=PA(),f=0,d=[];for(let p of e||[]){let h=await F(p),w=xr(h,{boxLabels:gc(p)});if(w.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let g of w.unpricedModels||[])l.add(g);DA(u,w.totalTokens);for(let[g,k]of Object.entries(w.byModel))n[g]||={usd:0,events:0,tokens:PA()},n[g].usd=ld(n[g].usd+k.usd),n[g].events+=k.events,DA(n[g].tokens,k.tokens);for(let[g,k]of Object.entries(w.byProvider))r[g]||={usd:0,events:0},r[g].usd=ld(r[g].usd+k.usd),r[g].events+=k.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,h)=>h.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:bs(),fleet:{totalUsd:ld(o),asRecordedUsd:ld(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:t}}var Vr=e=>`$${e.toFixed(4)}`,ca=(e,t)=>String(e).padEnd(t);function BA(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];if(r.push(`fleet total ${Vr(t.totalUsd)} over ${t.turns} turn(s) \xB7 ${t.tracksWithCost} track(s) with cost, ${t.tracksSilentArchived??t.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),t.repricedTurns&&r.push(`${t.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),t.asRecordedUsd!=null&&Math.abs(t.asRecordedUsd-t.totalUsd)>=.005&&r.push(`as-recorded total ${Vr(t.asRecordedUsd)} \xB7 current-rates total ${Vr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Vr(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${ca(a,26)} ${ca(Vr(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${ca(a.name||a.id,30)} ${Vr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ca(c.label,40)} ${ca(Vr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${ca(Vr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
677
|
+
`)}me();Wp();import XU from"node:fs/promises";import ZU from"node:path";kl();vr();var ud=e=>e.payload?.ts??e.ts,VU=12e4,zr=e=>Math.round(e*1e4)/1e4,zU=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function YU(e,{since:t}){let{track:n,events:r,brief:o}=e,s=S=>ud(S)>=t,a=Pn([ri(n?.name),ri(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let E=Pn(S.payload.task);E&&i.set(S.boxId,E)}let c=new Map,l=S=>{let E=S||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((S,E)=>ud(S)-ud(E)),d=0,p=0,h=0,w={turns:0};for(let S of f){let E=l(S.boxId),R=E.segments[E.segments.length-1],B=ud(S);switch(E.lastTs!=null&&B>E.lastTs&&s(S)&&(R.activeMs+=Math.min(B-E.lastTs,VU)),E.lastTs=B,S.provider&&(E.provider=S.provider),S.type){case"turn.user":{let T=S.payload?.text;if(S.source==="hook"||!String(T||"").trim())break;let _=u(Pn(T));E.segments.push(_),s(S)&&(_.turns+=1);break}case"peer.message":{let T=S.payload?.text;if(!String(T||"").trim())break;let _=u(Pn(T));E.segments.push(_),s(S)&&(_.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let T=S.payload?.tool;T&&(R.tools[T]=(R.tools[T]||0)+1),s(S)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let{usd:T,tokens:_,model:O,unpriced:I}=Ft(S);I&&(w.turns+=1),R.usd+=T,_&&(R.tokens.input+=_.input,R.tokens.output+=_.output,R.tokens.cacheRead+=_.cacheRead,R.tokens.cacheWrite+=_.cacheWrite),R.models[O]=(R.models[O]||0)+T,S.provider&&(R.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let g=new Map,k=!1;for(let[S,E]of c)for(let R of E.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let T=R.textCat?.category??i.get(S)?.category??Ub(R.tools)?.category??a?.category??"uncategorized";g.has(T)||g.set(T,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let _=g.get(T);_.usd+=R.usd,_.activeMs+=R.activeMs,_.turns+=R.turns,_.toolCalls+=R.toolCalls;for(let[I,U]of Object.entries(R.models))_.models[I]=(_.models[I]||0)+U;let O=R.provider||E.provider;O&&(_.providers[O]=(_.providers[O]||0)+R.usd),O==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:g,handoffs:d,merged:p,failed:h,sawCursorActivity:k,unpricedTurns:w.turns}}function LA(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let w of e||[]){let{track:g,events:k}=w;if(!k?.length){a+=1;continue}let{perCategory:S,handoffs:E,merged:R,failed:B,sawCursorActivity:T,unpricedTurns:_}=YU(w,{since:r});T&&(i=!0),c+=_;let O={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:R,subtasksFailed:B};for(let[I,U]of S){O.usd+=U.usd,O.activeMs+=U.activeMs,O.turns+=U.turns,O.categories[I]={usd:zr(U.usd),activeMs:U.activeMs};for(let[X,C]of Object.entries(U.models))O.models[X]=zr((O.models[X]||0)+C);for(let[X,C]of Object.entries(U.providers))O.providers[X]=zr((O.providers[X]||0)+C);o.has(I)||o.set(I,zU());let H=o.get(I);H.usd+=U.usd,H.activeMs+=U.activeMs,H.turns+=U.turns,H.toolCalls+=U.toolCalls;for(let[X,C]of Object.entries(U.models))H.models[X]=(H.models[X]||0)+C;for(let[X,C]of Object.entries(U.providers))H.providers[X]=(H.providers[X]||0)+C;let ae=H.byTrack.get(g?.id)||{usd:0,activeMs:0};ae.usd+=U.usd,ae.activeMs+=U.activeMs,H.byTrack.set(g?.id,ae)}(O.usd>0||O.activeMs>0||O.turns>0)&&(O.usd=zr(O.usd),O.dominantCategory=JU(O.categories),s.push(O))}let l=[...o.values()].reduce((w,g)=>w+g.usd,0),u=[...o.values()].reduce((w,g)=>w+g.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),h=[...o.entries()].map(([w,g])=>({category:w,sharePct:d>0?Math.round((f==="usd"?g.usd:g.activeMs)/d*100):0,shareBasis:f,usd:zr(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:jA(g.models),providers:jA(g.providers),topWorkstreams:[...g.byTrack.entries()].sort((k,S)=>S[1].usd-k[1].usd||S[1].activeMs-k[1].activeMs).slice(0,2).map(([k,S])=>({trackId:k,name:p.get(k),usd:zr(S.usd),activeMs:S.activeMs}))})).sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs);return s.sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:zr(l),activeMs:u,turns:[...o.values()].reduce((w,g)=>w+g.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var JU=e=>{let t=null;for(let[n,r]of Object.entries(e))(!t||r.usd>t.v.usd||r.usd===t.v.usd&&r.activeMs>t.v.activeMs)&&(t={cat:n,v:r});return t?.cat??null},jA=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,zr(n)]));var la={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function QU(e){let t=String(e||"7d");return t in la?{key:t,ms:la[t]}:{key:"7d",ms:la["7d"]}}function e4(e,t){let n=new Set((t||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...t||[]];for(let o of e?.events||[]){let s=Wl(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:e.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function t4(e){if(!e?.cwd)return null;try{let n=(await XU.readFile(ZU.join(e.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function FA(e,{window:t="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:QU(t),f=[];for(let d of e||[]){let p=await F(d),h=e4(d,p);i&&c<o&&(h=h.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:h,brief:await t4(d)})}return{window:l,...LA(f,{windowMs:u,now:c})}}Dn();var Yr=36e5,qA=24*Yr,n4=400,r4=12,Ko=e=>Math.round(e*1e4)/1e4;function zw({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in la?String(e||"7d"):"7d",i=la[a];return{key:a,since:i==null?null:r-i,until:r}}var o4=(e,t)=>e!=null&&t-e<=48*Yr?Yr:qA;function UA(e,t){if(t===Yr)return Math.floor(e/Yr)*Yr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function s4(e,t){if(t===Yr)return e+Yr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var HA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),WA=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},a4=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ko(n)]));async function KA(e,t={}){let n=t.now??Date.now(),r=zw({...t,now:n}),o=r.since==null?qA:o4(r.since,r.until),s=I=>(r.since==null||I>=r.since)&&I<r.until,a=r.since!=null?r.until-r.since:null,i=I=>a!=null&&I>=r.since-a&&I<r.since,c={},l={},u=HA(),f=0,d=0,p=0,h=0,w=0,g=0,k=new Set,S=new Map,E=[],R=[],B=[],T=null;for(let I of e||[]){let H=(await F(I)).filter(C=>C.type==="cost.recorded");if(!H.length){I.archivedAt||B.push(I.name||I.id);continue}let ae=[];for(let C of H){let Q=C.ts??0;s(Q)?ae.push(C):i(Q)&&(g+=Ft(C).usd)}if(!ae.length)continue;let X=xr(ae,{boxLabels:gc(I)});f+=X.totalUsd,d+=X.turns,p+=X.repricedTurns||0,h+=X.asRecordedUsd||0,w+=X.unpricedTurns||0;for(let C of X.unpricedModels||[])k.add(C);WA(u,X.totalTokens);for(let[C,Q]of Object.entries(X.byModel))c[C]||={usd:0,events:0,tokens:HA()},c[C].usd=Ko(c[C].usd+Q.usd),c[C].events+=Q.events,WA(c[C].tokens,Q.tokens);for(let[C,Q]of Object.entries(X.byProvider))l[C]||={usd:0,events:0},l[C].usd=Ko(l[C].usd+Q.usd),l[C].events+=Q.events;for(let C of ae){let{usd:Q}=Ft(C);if(!Q)continue;let ve=UA(C.ts??0,o),W=S.get(ve)??{t:ve,total:0,byProvider:{}};W.total+=Q;let be=C.provider||"unknown";W.byProvider[be]=(W.byProvider[be]||0)+Q,S.set(ve,W),(T==null||ve<T)&&(T=ve)}E.push({id:I.id,name:I.name||I.id,archived:!!I.archivedAt,usd:X.totalUsd,turns:X.turns,providers:Object.keys(X.byProvider)});for(let[C,Q]of Object.entries(X.byBox))R.push({trackId:I.id,trackName:I.name||I.id,boxId:C,...Q})}let _=r.since!=null?UA(r.since,o):T,O=[];for(let I=_,U=0;I!=null&&I<r.until&&U<n4;I=s4(I,o),U++){let H=S.get(I);O.push(H?{t:I,total:Ko(H.total),byProvider:a4(H.byProvider)}:{t:I,total:0,byProvider:{}})}return E.sort((I,U)=>U.usd-I.usd),R.sort((I,U)=>U.usd-I.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:bs(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:O,fleet:{totalUsd:Ko(f),asRecordedUsd:Ko(h),turns:d,repricedTurns:p,unpricedTurns:w,unpricedModels:[...k],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:E.length,tracksSilentLive:B},prior:a!=null?{totalUsd:Ko(g),since:r.since-a,until:r.since}:null,tracks:E.slice(0,50),boxes:R.slice(0,r4)}}wu();var i4=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),c4=20,l4=40;async function GA(e,t={}){let n=t.now??Date.now(),r=zw({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await F(l),f=0,d=gc(l);for(let p of u){if(!i4.has(p.type))continue;let h=p.type==="code.rework"?p.payload?.ts??p.ts:p.ts;p.type!=="rework.cause.answered"&&!o(h??0)||(p.type==="code.committed"&&f++,s.push(p.trackId?p:{...p,trackId:l.id}))}f&&i++;for(let p of l.boxes||[])a[`${l.id}/${p.name}`]={trackName:l.name||l.id,label:d[p.name]||p.name}}let c=wS(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:Dr,decayEndDays:$m,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,c4),fixes:c.fixes.slice(0,l4)}}import u4 from"node:fs";import VA from"node:fs/promises";import yc from"node:os";import d4 from"node:path";Gm();lt();var f4=1024*1024,dd=e=>Math.round(e/f4);async function p4(e){try{let t=await VA.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:dd(r),diskTotalMb:dd(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function m4(){try{return(await zt()).filter(t=>t.isBostrat).length}catch{return null}}async function h4(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await VA.readdir(d4.join(sc,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function zA({stateDir:e=sc,now:t=Date.now}={}){let n=yc.totalmem(),[r,o,s,a]=await Promise.all([Su().catch(()=>null),p4(u4.existsSync(e)?e:yc.tmpdir()),m4(),h4()]),i=yc.cpus()?.length||null,c=yc.loadavg()?.[0]??null;return{ts:t(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?dd(n-r):null,memTotalMb:dd(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(yc.uptime())}}me();Hf();Oa();Dn();me();Dn();var w4=864e5;function g4(e,{tolerance:t=.005}={}){if(e.type!=="cost.recorded"||e.source==="vendor")return null;let n=e.payload?.tokens,r=Number(e.payload?.usd)||0;if(!(n?(n.input||0)+(n.output||0)+(n.cacheRead||0)+(n.cacheWrite??n.cacheCreation??0):0)||!r)return null;let s=e.payload?.model||"unknown",a=e.payload?.speed||void 0,i={input:n.input||0,output:n.output||0,cacheRead:n.cacheRead||0,cacheWrite:n.cacheWrite??n.cacheCreation??0},c=mn(s,i,e.ts,a?{speed:a}:void 0);if(!c)return null;let l=r-c,u=Math.abs(l)>Math.max(c*t,.001);return{model:s,speed:a??null,ts:e.ts,asRecorded:r,expected:c,delta:l,divergent:u}}async function YA(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*w4,s={},a=0,i=0,c=[];for(let u of e||[]){let f=await F(u);for(let d of f){if(!d.ts||d.ts<o)continue;let p=g4(d,{tolerance:n});if(!p)continue;a++;let h=p.speed?`${p.model} (${p.speed})`:p.model,w=s[h]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};w.rows++,w.asRecordedUsd+=p.asRecorded,w.expectedUsd+=p.expected,p.divergent&&(i++,w.divergent++,c.length<10&&c.push({track:u.name||u.id,...p}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}Em();Fl();it();Bn();import ua from"node:fs/promises";import y4 from"node:fs";import ft from"node:path";import b4 from"node:os";import{execFile as k4}from"node:child_process";import{promisify as x4}from"node:util";var v4=x4(k4),JA=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||b4.tmpdir(),".bostrat"),fd=!1,Yw=null;async function Jw(e,t){let n=await ua.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ua.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ua.mkdir(ft.dirname(t),{recursive:!0}),await ua.copyFile(e,t),!0)}var S4=new Set([".claude",".codex",".cursor",".credentials.json","auth.json",".claude.json","api-token","node.env","certs","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function pd(e,t,{skip:n=null}={}){let r=0,o=await ua.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(S4.has(s.name)||n?.has(s.name))continue;let a=ft.join(e,s.name);s.isDirectory()?r+=await pd(a,ft.join(t,s.name)):s.isFile()&&(r+=await Jw(a,ft.join(t,s.name))?1:0)}return r}async function XA({stateDir:e=JA,dest:t=process.env.BOSTRAT_SYNC_DIR||`${JA}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ua.mkdir(t,{recursive:!0});let o=0;o+=await Jw(ft.join(e,"tracks.json"),ft.join(t,"tracks.json"))?1:0,o+=await Jw(ft.join(e,"events.jsonl"),ft.join(t,"events.jsonl"))?1:0,o+=await pd(ft.join(e,"events"),ft.join(t,"events")),o+=await pd(ft.join(e,"memory"),ft.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await pd(ft.join(u.cwd,".bostrat"),ft.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>v4("git",["-C",t,...u]);y4.existsSync(ft.join(t,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Wd(t).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||fd))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,fd&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),fd=!1,Yw=null}catch(u){fd=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
678
|
+
`).slice(-3).join(" | ").slice(0,300);f!==Yw&&(Yw=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}dh();Xt();io();lt();Ta();import{execFile as T4}from"node:child_process";import{promisify as E4}from"node:util";import{randomUUID as A4}from"node:crypto";import _4 from"node:fs";import I4 from"node:os";import $4 from"node:path";var Xr=E4(T4),md=e=>new Promise(t=>setTimeout(t,e)),Zr=ff,Xw=process.env.BOSTRAT_USAGE_PROBE_CWD||$4.join(I4.homedir(),".bostrat","usage-probe"),R4=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,O4=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,C4=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,ZA=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Jr=null,bc=null,kc=null;async function M4(){try{return await Xr(K,V("has-session","-t",Zr)),!0}catch{return!1}}function N4(){Jr&&clearTimeout(Jr),Jr=setTimeout(()=>P4().catch(()=>{}),R4),Jr.unref&&Jr.unref()}async function P4(){Jr&&(clearTimeout(Jr),Jr=null);try{await Xr(K,V("kill-session","-t",Zr))}catch{}}async function D4(){let e=Date.now()+C4;for(;;){let t=await Zw().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await md(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await md(500)}}async function B4(){if(await M4())return;if(!await fr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");_4.mkdirSync(Xw,{recursive:!0}),await or(Xw);let e=A4();await Xr(K,V("new-session","-d","-s",Zr,"-c",Xw,"-x","200","-y","60",...Fa()));let t=`${df} --session-id ${e}`;await Xr(K,V("send-keys","-t",Zr,t,"Enter")),await D4()}async function Zw(){let{stdout:e}=await Xr(K,V("capture-pane","-t",Zr,"-p"));return e}async function j4(){await B4(),await Xr(K,V("send-keys","-t",Zr,"-l","/usage")),await Xr(K,V("send-keys","-t",Zr,"Enter"));let e=Date.now()+O4,t="";for(;await md(400),t=await Zw(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await md(900),t=await Zw(),await Xr(K,V("send-keys","-t",Zr,"Escape")).catch(()=>{}),N4(),t}function L4(e){let t=String(e||"").split(`
|
|
679
|
+
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,t.length)&&!/^Current\s/i.test(t[c]);c++){let l=t[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=t[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=t.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return t.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function F4(e){let t=String(e||"").split(`
|
|
680
|
+
`).map(s=>s.replace(/\s+$/,"")),n=t.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<t.length&&!r.test(t[s]);s++)t[s].trim()&&o.push(t[s].trim());return o.length?o:null}async function QA({fresh:e=!1}={}){return!e&&bc&&Date.now()-bc.at<ZA?bc.data:kc||(kc=(async()=>{try{let t=await j4(),n=L4(t),r={...n,contributors:F4(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return bc={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 bc={at:Date.now()-(ZA-3e3),data:n},n}finally{kc=null}})(),kc)}var U4=262144;function H4(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 W4=new Set([300]),q4=new Set([10080]);function K4(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:W4.has(t)?"session":q4.has(t)?"week":"other"}function G4(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 V4(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?H4((o-n)/1e3):null,resetsAt:o}}var xc=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function z4(e,t=Date.now()){let n=String(e||"").split(`
|
|
681
|
+
`),r=!1;for(let o=n.length-1;o>=0;o--){let s=n[o].trim();if(!s||s[0]!=="{"||!s.includes("token_count"))continue;let a;try{a=JSON.parse(s)}catch{continue}let i=a?.payload;if(i?.type!=="token_count")continue;r=!0;let c=i.rate_limits;if(!c)return xc(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=xc(r);[c.primary,c.secondary].forEach((p,h)=>{let w=V4(p,l,t);if(!w)return;let g=K4(p.window_minutes);g===null&&(g=h===0?"session":"week"),g==="session"&&!u.session?u.session=w:g==="week"&&!u.week?u.week=w:(u.other??=[]).push({label:G4(p.window_minutes),...w})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let d=c.credits;return d&&(d.has_credits||d.unlimited)&&(u.credits={balance:String(d.balance??""),unlimited:!!d.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...xc(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return xc(r)}return xc(r)}async function Qw(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await so(e?.cwd,e?.sessionId);if(!n)return{...t,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await Et(n.file,U4),o=z4(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 e_(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 hd({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 Qw(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Qw(null))}return n.has("cursor")&&r.push(await hd({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function hd(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?Qw(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await QA({fresh:t})}}ar();it();it();function Y4(e=process.env){return!!(Ie(e)&&e.BOSTRAT_PLANE_TOKEN)}var J4=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),wd=(e,t)=>Object.assign(new Error(e),{status:t});async function t_(e,t){if(!Y4(t))throw wd("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Ie(t)}${e}`,{headers:J4(t)})}catch(o){throw wd(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw wd(r?.error||`plane replied ${n.status}`,n.status);return r}async function n_(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await t_(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function r_(e,{env:t=process.env}={}){if(!e)throw wd("message id required",400);return(await t_(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}gi();eu();function eg(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 X4(e){let[t,n]=e.split("/"),r=eg(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 o_(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(X4).filter(Boolean)}function Z4(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function s_(e){let t=o_(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:Z4,allowed(r){if(n)return!0;let o=eg((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function a_(e,t){let n=o_(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=eg(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import i_ from"node:crypto";import gd from"node:fs";import Q4 from"node:path";function c_(e=process.env,{stateDir:t=sc,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=Q4.join(t,"api-token");try{let a=gd.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=i_.randomBytes(32).toString("base64url");try{gd.mkdirSync(t,{recursive:!0}),gd.writeFileSync(o,s+`
|
|
682
|
+
`,{mode:384}),gd.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function vc(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 l_(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:i_.timingSafeEqual(n,r)}function u_(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 e2=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],t2=e=>e2.some(t=>t.test(e));function d_({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=u_(o);return l_(c,e)?a():t2(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||vc(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 f_(e,t,{enforce:n=!0}={}){return!n||l_(u_(e),t)?!0:vc(e)}dn();xu();xu();import n2 from"node:http";import r2 from"node:net";function yd(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 bd(e){let t=yd(e.headers.host);return t?{slug:t,view:Um(t)}:null}var o2=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),s2=e=>{let t=o2(e.name);return`<!doctype html><meta charset="utf-8">
|
|
644
683
|
<meta http-equiv="refresh" content="3">
|
|
645
684
|
<title>${t} \u2014 starting</title>
|
|
646
685
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
647
686
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
648
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
687
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function p_(e,t,n){let r=bd(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(s2(o));if(o.state!=="live"&&o.state!=="unhealthy")return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...e.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":e.headers.host,"x-forwarded-proto":e.headers["x-forwarded-proto"]||"http"};delete a.authorization,delete a.cookie;let i=n2.request({host:"127.0.0.1",port:o.port,method:e.method,path:e.url,headers:a},c=>{t.writeHead(c.statusCode||502,c.headers),c.pipe(t)});i.on("error",()=>{t.headersSent?t.destroy():(t.statusCode=502,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),e.pipe(i),e.on("aborted",()=>i.destroy())}function m_(e,t,n){let r=bd(e);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return t.write(`HTTP/1.1 404 Not Found\r
|
|
649
688
|
\r
|
|
650
689
|
bostrat: no live view "${s}"\r
|
|
651
|
-
`),t.destroy(),!0;let a=
|
|
690
|
+
`),t.destroy(),!0;let a=r2.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${e.url} HTTP/1.1`];for(let[l,u]of Object.entries(e.headers))l!=="host"&&(l==="authorization"||l==="cookie"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
652
691
|
`)+`\r
|
|
653
692
|
\r
|
|
654
|
-
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function jE(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${h4(n.host)}/`}function h4(e){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let t=String(e||"").replace(/:\d+$/,"").toLowerCase();if(!t||!t.includes(".")||/^[\d.]+$/.test(t))return"bostrat.globopi";let n=t.split(".");return ad(t)&&Em(n[0])?n.slice(1).join("."):t}js();Lm();gu();rm();ue();import LE from"node:os";import{randomUUID as Uw}from"node:crypto";var w4=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||LE.hostname(),nodeName:e.BOSTRAT_NODE_NAME||LE.hostname()});var FE=32768,UE=120,HE=1e3,WE=new Set(["claude","codex","cursor"]),g4=new Set(["off","transition","nag"]),y4=[[0,59],[0,23],[1,31],[1,12],[0,6]];function b4(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=y4[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var Qt=e=>String(e??"").trim(),No={name(e){let t=Qt(e);if(!t)throw new Error("a loop needs a name");if(t.length>UE)throw new Error(`name is too long (${t.length}); cap is ${UE}`);return t},schedule(e){let t=Qt(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!b4(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Qt(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>FE)throw new Error(`prompt is too long (${t.length} chars); cap is ${FE}`);return t},expectation(e){let t=Qt(e)||null;if(t&&t.length>HE)throw new Error(`expectation is too long (${t.length}); cap is ${HE}`);return t},notify(e){let t=Qt(e)||"off";if(!g4.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Qt(typeof t=="string"?t:t?.provider)||"claude";if(!WE.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...WE].join(", ")}`);let r=typeof t=="object"&&t&&Qt(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function k4(e,t){let n=e&&typeof e=="object"?e:{},r=No.name(n.name),o=No.schedule(n.schedule),s=No.prompt(n.prompt),a=No.expectation(n.expectation),i=No.notify(n.notify),c=No.assignment(n.assignment),l=Qt(t?.workspace?.repo)||Qt(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:d}=w4();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function KE(e,t){let n=k4(t,e),r=Uw();return await O({id:e.id,cwd:e.cwd},{type:"loop.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:r,...n}}),{proposalId:r,...n}}async function GE(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await F({id:e.id,cwd:e.cwd}),a=s.find(c=>c.type==="loop.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="loop.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await O({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("loopId required \u2014 the loop is created on the plane before the card is answered");let i=a.payload;return await O({id:e.id,cwd:e.cwd},{type:"loop.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:String(r),name:i.name,schedule:i.schedule,provider:i.assignment?.provider??"claude",repo:i.repo??null,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await O({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}var Fw=["name","schedule","prompt","expectation","notify","assignment","enabled"],x4=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function VE(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function zE(e,t){let n=Qt(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function v4(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!n)throw new Error("a patch must be a JSON object of the fields to change");let r=Object.keys(n).filter(a=>!Fw.includes(a));if(r.length){let a=r.filter(i=>["repo","nodeId","cwd","userId","owner","id","trackId"].includes(i));throw a.length?new Error(`a proposal can't change ${a.join(", ")} \u2014 retargeting a loop is a Loops-tab edit (scope D5)`):new Error(`unknown field(s) ${r.join(", ")} \u2014 patchable: ${Fw.join(", ")}`)}let o={};for(let a of Fw){if(!(a in n))continue;let i=No[a](n[a]);(a==="assignment"?x4(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function JE(e,t,n){let r=VE(t),{patch:o,changed:s}=v4(n,r),a=Uw(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await O({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var qE=600;async function YE(e,t,n){let r=Qt(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>qE)throw new Error(`reason is too long (${r.length}); cap is ${qE}`);let o=VE(t),s=Uw(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await O({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var XE=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function ZE(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await XE(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await O(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await O(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function QE(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await XE(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await O(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await O(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}sr();ue();Pn();gu();var e_=10,t_=200,S4=120,T4=24,r_=/^\d{4}-\d{2}-\d{2}$/;function Hw(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var A4=(e=Date.now())=>new Date(e).toISOString().slice(0,10),st=(e,t="i")=>new RegExp(e,t),E4=[{re:st("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:st("\\bSPC\\b",""),reason:"business (SPC)"},{re:st("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:st("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:st("\\bReplicas\\b",""),reason:"competitor research"},{re:st("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:st("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:st("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],_4=[st("\\bSPC\\b",""),st("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),st("competitor|cloudcli|\\bkiro\\b"),st("\\bReplicas\\b",""),st("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),st("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),st("(^|[^\\w&])#\\d+\\b"),st("\\btrack/[\\w.-]+")];function I4({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of E4)if(o.test(n)||o.test(r))return s;return null}function n_(e){let t=String(e||"");for(let n of _4){let r=n.exec(t);if(r)return r[0].trim()}return null}function o_(e,t=Date.now()){if(!e)return t-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(e).trim());if(n)return t-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(e);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${e}" \u2014 use 24h, 7d, or an ISO date`)}var $4=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function R4(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Ri(e);if(t.length)throw new Error(`changelog entry appears to contain a secret VALUE (${t.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of e.split(`
|
|
655
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,S4)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${T4}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=$4.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let d=n_(i);if(d)throw new Error(`bullet ${r.length+1} mentions "${d}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>t_)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${t_}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=n_(n);if(o)throw new Error(`headline mentions "${o}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items; rewrite it and re-publish.`)}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>e_)throw new Error(`${r.length} bullets \u2014 over the ${e_}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function O4(e,{repos:t=Hw()}={}){let n=new Set(t);return(await po()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:I4({title:r.title,headRef:r.headRef})}))}async function s_(e,t){let n=await O4(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function a_(e,{md:t,day:n=null}){let r=R4(t),o=n==null||n===""?A4():String(n);if(!r_.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await O({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}async function i_(e,{day:t}){let n=String(t||"");if(!r_.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await O({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}$s();Pn();Cs();ue();$s();it();_r();Pn();di();Gl();gi();Cn();import{execFile as C4}from"node:child_process";import{promisify as M4}from"node:util";var qw=M4(C4),c_=3e4,N4=30*6e4,P4=c_,D4=10*6e4,B4=10080*6e4,j4=60*6e4,L4=8,F4=1440*6e4,U4=3,l_="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Ww=60*6e4,H4=15*6e4,W4=9e4,q4=1440*6e4,cd=new Map,ld=new Map,K4=/could not resolve|not found|no pull requests?/i,G4=e=>K4.test(String(e?.message||e||""));function V4(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Ww)}var u_={fast:P4,slow:D4,decay:j4};function d_(e,t){return e.armedAt&&t-e.armedAt<N4||V4(e,t)||f_(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>B4?"decay":"slow"}function z4(e,t){return u_[d_(e,t)]}function wc(e,t,n,r){let o=e.lanes?.get(re(t.repo,t.number))||{};return Kl({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function J4(e,t){Bn("watch");try{let{stdout:n}=await qw("gh",["pr","view",String(t),"--repo",e,"--json",l_],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await tn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Tr(n)&&Ar(n),n}}async function Y4(e){Bn("watch");try{let{stdout:t}=await qw("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",l_],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await tn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Tr(t)&&Ar(t),t}}async function X4(){let e=new Map;for(let t of await ie().catch(()=>[]))e.set(t.id,t);return e}async function Z4(e,t){await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function Q4(e,t){let n=await ho(e);return n?_s(n,t):null}async function e2(e,t,n){let r=await ho(e);if(!r)throw new Error(`no local checkout for ${e}`);return Is(r,t,{method:n})}async function t2(e,t){let n=await ho(e);if(!n)throw new Error(`no local checkout for ${e}`);return mi(n,t)}async function n2(e,t){await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function oa(e,t,n,r,o){if(!await r.disarm(e.repo,e.number).catch(()=>null))return;let a=n.get(e.trackId);a&&await r.emitDisarmed(a,{number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo,reason:o}).catch(()=>{})}var r2=10*6e4;async function o2(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>F4){await oa(e,null,t,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(e.repo,e.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await oa(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await oa(e,o,t,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(e.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<r2)return;await oa(e,o,t,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(e.repo,e.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(e.repo,e.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await oa(e,o,t,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(e.repo,e.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await oa(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await p_(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function sa(e,t,n){await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function s2(e,t){try{return await qw("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function hc(e,t,n,r=()=>!0){return(await F(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var aa=(e,t)=>({number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo});function f_(e,t,{grace:n=W4}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=q4}async function a2(e,t,n,r){let o=r.now??Date.now();if(!f_(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||qe(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??wo)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await F(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??Nn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??Nn)(e.repo,e.number).catch(()=>null)?(await sa(s,"opened",{...aa(e,t),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${e.repo}#${e.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function i2(e,t,n,r){let o=r.now??Date.now(),s=t.headRefOid||null,a=n.get(e.trackId)||null;if(s&&(e.ciFix?.headSha&&s!==e.ciFix.headSha&&await Bl(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Ll(e.repo,e.number,{headSha:s}).catch(()=>{})),s&&s!==e.lastHeadSha){let i=!e.lastHeadSha,c=!!(e.ciFix||e.reconcile||e.ciFixAwait||e.reconcileAwait),l=!!e.baseFoldAt&&o-e.baseFoldAt<H4;await wx(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await hx(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await hc(a,"pushed",e.number,d=>d.payload?.sha===s)&&await sa(a,"pushed",{...aa(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Ww)await jl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await jl(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=wc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await hc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await sa(a,"ci-fixed",{...aa(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await jl(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Ww)await Fl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Fl(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){wc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await hc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await sa(a,"reconciled",{...aa(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Fl(e.repo,e.number).catch(()=>{})}}}}async function c2(e,t,n,r){let o=wc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await hc(s,"closed",e.number)&&await sa(s,"closed",{...aa(e,t),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(e.repo,e.number,"closed").catch(()=>{}),await r.mark(e.repo,e.number,"closed").catch(()=>{})}async function l2(e,t,n){wc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await hc(r,"lost",e.number)&&await sa(r,"lost",aa(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function p_(e,t,n,r,o={}){let s=wc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await F(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function u2(e={}){let{now:t=Date.now(),auth:n=Wp,view:r=J4,list:o=Y4,links:s=bx,trackMap:a=X4,emitMerged:i=Z4,mark:c=vr,archiveIfArmed:l=Ln,notifyIfArmed:u=Fn,notifyIfResolved:d=Iu,batchThreshold:f=U4,detail:p=Q4,merge:m=e2,updateBranch:w=t2,lookup:y=Qe,disarm:x=ui,emitDisarmed:T=n2,localHasCommit:I=s2,claim:$=Nn,midTurn:D=wo}=e;if(!(await n().catch(()=>null))?.authenticated||Wl()>0)return 0;let A=await s().catch(()=>[]),R=Wx(A),E=A.filter(Y=>t-(cd.get(re(Y.repo,Y.number))||0)>=z4(Y,t));if(!E.length)return 0;let L=Date.now(),U=new Map,se=new Map;for(let Y of[...E].sort(hi)){let te=d_(Y,t);U.set(re(Y.repo,Y.number),{lane:te,intervalMs:u_[te]}),cd.set(re(Y.repo,Y.number),t),se.has(Y.repo)||se.set(Y.repo,[]),se.get(Y.repo).push(Y)}let G=await a().catch(()=>new Map),C={now:t,lanes:U,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:m,updateBranch:w,lookup:y,disarm:x,emitDisarmed:T,localHasCommit:I,claim:$,midTurn:D};for(let[Y,te]of se){let we,ce=new Set;if(te.length>f)we=await o(Y).catch(()=>null);else{we=new Map;for(let V of te)try{let ge=await r(Y,V.number);ge?.number!=null&&we.set(Number(ge.number),ge)}catch(ge){G4(ge)&&ce.add(re(V.repo,V.number))}}if(we)for(let V of te){let ge=re(V.repo,V.number),B=we.get(Number(V.number));if(!B){if(ce.has(ge)){let ye=(ld.get(ge)||0)+1;ld.set(ge,ye),ye>=L4&&(await l2(V,G,C),ld.delete(ge),cd.delete(ge))}continue}if(ld.delete(ge),B.state==="MERGED"||B.state==="CLOSED")B.state==="MERGED"?await p_(V,B,G,C):await c2(V,B,G,C),cd.delete(ge);else{await a2(V,B,G,C).catch(()=>{}),await i2(V,B,G,C).catch(()=>{});let ye=!V.trackId||R.get(V.trackId)===ge;V.autoMerge&&ye&&await o2(V,G,C).catch(()=>{})}}}let Z={};for(let{lane:Y}of U.values())Z[Y]=(Z[Y]||0)+1;return console.log(`[pr-watch] probed ${E.length} link(s) (${Object.entries(Z).map(([Y,te])=>`${Y} ${te}`).join(", ")}) in ${Date.now()-L}ms`),E.length}function m_(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await u2()}catch{}finally{e=!1}}},c_)}ue();Pn();tr();_r();Cn();function h_(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function d2(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var f2=new Set(["merged","closed","lost"]);function p2(e,t){if(!e?.blocked)return null;let n=h_(t);if(e.blocked==="red")return t.checks.failed===0&&t.checks.total>0?{change:"ci-fixed",reason:"red"}:null;if(e.blocked==="conflict")return t.mergeable==="MERGEABLE"&&n!=="red"?{change:"reconciled",reason:"conflict"}:null;if(e.blocked==="behind"){let r=t.headSha??null;return n===null&&r&&e.sha&&r!==e.sha?{change:"reconciled",reason:"behind"}:null}return null}function m2(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function h2(e,t,n,r,o){return(await F(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function w2(e,t,n){await O({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function w_(e,t={}){let{now:n=Date.now(),slugOf:r=xe,lookup:o=Qe,note:s=gx,tracks:a=async()=>(await ie()).filter(d=>!qe(d)),asked:i=h2,emitChange:c=w2}=t,l=[],u=null;for(let d of e??[]){if(!d?.prs?.length)continue;let f=await r(d.path).catch(()=>null);if(f)for(let p of d.prs)try{if(!d2(p))continue;let m=await o(f,p.number).catch(()=>null);if(!m?.trackId||f2.has(m.state))continue;let{prev:w}=await s(f,p.number,{blocked:h_(p),sha:p.headSha??null,at:n});if(!w)continue;let y=p2(w,p);if(!y||m2(m,y.change))continue;u??=new Map((await a()).map($=>[$.id,$]));let x=u.get(m.trackId);if(!x)continue;let T=p.headSha??null;if(await i(x,y.change,p.number,T,w.at||0))continue;let I={number:Number(p.number),url:p.url||m.url,title:p.title||m.title,branch:p.headRefName||m.headRef,repo:f,sha:T,delegated:!1,reason:y.reason};y.change==="ci-fixed"?I.passed=p.checks.passed:I.base=p.baseRefName||null,await c(x,y.change,I),l.push({change:y.change,number:p.number,reason:y.reason})}catch{}}return l}nt();ue();_r();Cn();import{randomUUID as g2}from"node:crypto";var y2=2e4,g_=3,ia=new Map,Kw=new Set;async function y_(e,t,n){let r=await n(e).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===t?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function b2(e={}){let{sessions:t=null,tracks:n=null,append:r=O,read:o=F,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Ut(),i=n??await ie()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(qe(u)||!u.leadBox)continue;let d=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let f=c.get(d.name);if(f)if(l.add(d.name),f.alternateOn){let p=(ia.get(d.name)||0)+1;ia.set(d.name,p),p===g_&&(Kw.add(d.name),await y_(u,d.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:g2(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(ia.get(d.name)||0)>=g_;if(ia.delete(d.name),p||!Kw.has(d.name)){Kw.add(d.name);let m=await y_(u,d.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ia.keys()])l.has(u)||ia.delete(u)}var gc=null,Gw=!1;function b_(){return gc?()=>{}:(gc=setInterval(()=>{Gw||(Gw=!0,b2().catch(()=>{}).finally(()=>{Gw=!1}))},y2),gc.unref?.(),()=>{clearInterval(gc),gc=null})}Ss();$m();ue();$s();Cs();Ss();import{randomUUID as k2}from"node:crypto";async function x2(){let e=await Promise.resolve().then(()=>(_r(),KT));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Vw={listRepoPrs:cn,readEvents:F,appendEvent:O,sayDeps:x2,currentBranch:Rs};async function v2(e,t=Vw,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Rs)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function zw(e,t,{actions:n,notify:r=!1}={},o=Vw){let s=t?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await v2(e,o,{branch:s.branch});if(!a)return null;let i={id:e.id,cwd:e.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=k2();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:c,srcOfferId:s.offerId??null,branch:s.branch,base:s.base??null,pr:a,actions:n,...r?{notify:!0}:{},offeredRebase:Array.isArray(s.offered)&&s.offered.includes("rebase")}}),{offerId:c,pr:a}}async function Jw(e,{offerId:t,choice:n}={},r=Vw){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=xs.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,vs(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,sx(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import Zn from"node:fs/promises";import ca from"node:path";import E_ from"node:os";import k_ from"node:fs/promises";import ud from"node:path";import{execFile as S2}from"node:child_process";import{promisify as T2}from"node:util";import{fileURLToPath as A2}from"node:url";var E2=T2(S2),v_=ud.dirname(A2(import.meta.url)),_2=ud.resolve(v_,"..","..","client","dist"),I2="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$2=new Map([...I2].map((e,t)=>[e,t]));function R2(e){let t=[],n=0,r=0;for(let o of e){let s=$2.get(o);if(s===void 0)return t;n+=(s&31)<<r,s&32?r+=5:(t.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return t}function O2(e){let t=[],n=0,r=0,o=0;for(let s of e.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=R2(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}t.push(a)}return t}function C2(e,t){if(!e||e.length===0||e[0][0]>t)return null;let n=0,r=e.length-1;for(;n<r;){let o=n+r+1>>1;e[o][0]<=t?n=o:r=o-1}return e[n]}var x_=new Map;async function M2(e){let t;try{t=await k_.stat(e)}catch{return null}let n=x_.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await k_.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:O2(r.mappings||"")};return x_.set(e,o),o}catch{return null}}var Yw=null;function N2(){return Yw||(Yw=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):E2("git",["rev-parse","--short","HEAD"],{cwd:ud.resolve(v_,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Yw}var P2=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function D2(e,{distDir:t=_2}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(P2)){let[a,i,c,l]=s,u=await M2(ud.join(t,"assets",`${i}.map`)),d=u?C2(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function S_(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await N2();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await D2(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var B2=()=>process.env.BOSTRAT_STATE_DIR||ca.join(process.env.HOME||E_.tmpdir(),".bostrat"),Gr=()=>ca.join(B2(),"telemetry"),__=()=>ca.join(Gr(),".relay-offsets.json"),j2=50,L2=14,F2=3e4,T_=500,It=(e,t)=>typeof e=="string"?e.slice(0,t):"";function U2(e,t){if(!e||typeof e!="object")return null;let n=It(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:It(e.anonId,64),sessionId:It(e.sessionId,64),userId:It(e.userId,64),release:It(e.release,64),appVersion:It(e.appVersion,32),route:It(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:It(e.error.kind,256),message:It(e.error.message,8192),stack:It(e.error.stack,32768),fingerprint:It(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:It(s?.n,64),r:It(s?.r,128)}))),o}var I_=e=>new Date(e).toISOString().slice(0,10),A_=0;async function $_(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,j2):[]).map(s=>U2(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await S_(s);await Zn.mkdir(Gr(),{recursive:!0});let o=ca.join(Gr(),`${I_(t)}.ndjson`);return await Zn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
656
|
-
`).join(""),"utf8"),t-
|
|
657
|
-
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=T_){let p=d.slice(f,f+T_).map(w=>W2(w)),m=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await C_(r)}return{sent:o,disabled:!1}}var Xw=null,Zw=!1;function M_(){Xw||(Xw=setInterval(async()=>{if(!Zw){Zw=!0;try{await q2()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Zw=!1}}},F2),Xw.unref?.())}import pd from"node:fs/promises";import j_ from"node:path";import K2 from"node:os";var L_=()=>process.env.BOSTRAT_STATE_DIR||j_.join(process.env.HOME||K2.tmpdir(),".bostrat"),F_=()=>j_.join(L_(),"dictation-glossary.json"),G2=100,V2=500,z2=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],md=null,Qw=null,eg=Promise.resolve();function N_(){let e={},t=Date.now();for(let n of z2)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function U_(){return md||(Qw??=pd.readFile(F_(),"utf8").then(e=>JSON.parse(e)?.terms??N_()).catch(()=>N_()).then(e=>md=e).finally(()=>Qw=null),Qw)}async function P_(){let e=JSON.stringify({terms:md},null,2),t=F_(),n=`${t}.${process.pid}.tmp`;await pd.mkdir(L_(),{recursive:!0}),await pd.writeFile(n,e),await pd.rename(n,t)}function J2(){return eg=eg.then(P_,P_),eg.catch(()=>{})}async function tg(){return Object.values(await U_()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var Y2=e=>Math.max(1,Math.ceil(e.length/4));async function X2(){let e=[],t=0;for(let{term:n}of await tg()){let r=Y2(n);if(!(t+r>V2)&&(e.push(n),t+=r,e.length>=G2))break}return e}async function Z2(e,t){let n=await U_(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var D_=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),fd=e=>e.toLowerCase();function B_(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function H_(e,t){let n=D_(e),r=D_(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=fd(n[u])===fd(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)fd(n[c])===fd(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(B_(r[l])&&i.add(r[l]),l++);for(;l<s;l++)B_(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await Z2(u,"learned");return await J2(),[...i]}var yc=null;function Q2(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return e===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${e}: ${t.slice(0,300)}`}}async function W_(){if(!yc)return null;let e=await nc();if(!e||e.apiKey.slice(-6)!==yc.keyTail)return null;let{keyTail:t,...n}=yc;return n}async function q_(e,t){let n=await nc();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await X2())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":t||"application/octet-stream"},body:e});if(!o.ok){let i=await o.text().catch(()=>""),c=Q2(o.status,i);c.persistent&&(yc={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return yc=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ue();var ng={"gpt-realtime-2.1":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime":{textIn:4,textInCached:.4,textOut:16,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2.1-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20},"gpt-realtime-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20}};function eH(e){if(!e)return null;let t=String(e).trim();if(ng[t])return t;let n=null;for(let r of Object.keys(ng))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function tH(e,t={}){let n=ng[eH(e)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(t[o])||0)*n[o];return r/1e6}function nH(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.error?.code||n?.error?.type||"";return e===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${e}): ${t.slice(0,300)}`}}var bc=null;async function rH(){if(!bc)return null;let e=await Ku();if(!e||e.apiKey.slice(-6)!==bc.keyTail)return null;let{keyTail:t,...n}=bc;return n}async function oH(e){let t=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:e.model,audio:{output:{voice:e.voice}}}})});if(!t.ok){let o=await t.text().catch(()=>""),s=nH(t.status,o);s.persistent&&(bc={code:s.code,message:s.message,at:Date.now(),keyTail:e.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}bc=null;let n=await t.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var sH={openai:oH};async function K_(){let e=await Ku();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await rH()}:{configured:!1}}async function G_(){let e=await Ku();if(!e){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let t=await sH[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function V_(e,{model:t,usage:n={},seconds:r}={}){let o=c=>Math.max(0,Math.round(Number(c)||0)),s={textIn:o(n.textIn),textInCached:o(n.textInCached),textOut:o(n.textOut),audioIn:o(n.audioIn),audioInCached:o(n.audioInCached),audioOut:o(n.audioOut)},a=tH(t,s),i=await O(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}Cn();var eI=ke.dirname(iH(import.meta.url)),z_=Number(process.env.PORT)||3362,wd=Xh(process.env),tI=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,la=IE(process.env.BOSTRAT_ALLOW_IPS),ig=$E(process.env.BOSTRAT_VIEW_FRONT_CIDRS,id),nI=OE(),rI=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",v=Lt();v.disable("x-powered-by");v.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),ad(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});v.use((e,t,n)=>{if(mc(e)||la.allowed(la.clientIp(e))||ig.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});v.use(DE);v.use(NE({token:nI,enforce:rI}));v.use("/api/mocks",Lt.json({limit:"1mb"}));v.use("/api/mocks",Lt.raw({type:"image/png",limit:"24mb"}));v.use("/api/mocks",Lt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));v.use("/api/runbooks",Lt.json({limit:"1mb"}));v.use("/api/tracks/adopt",Lt.json({limit:"1mb"}));v.use("/api/telemetry",Lt.json({limit:"256kb"}));v.use("/api/hook",Lt.json({limit:"2mb"}));v.use("/api/dictation",Lt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));v.use("/api/tracks/:id/upload",Lt.raw({type:()=>!0,limit:"25mb"}));v.use(Lt.json({limit:"64kb"}));v.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:xt,hostTmuxAlive:await ir(),autoTriggerPct:tI,plane:ua?ua.status():null})});v.get("/api/sessions",async(e,t)=>{t.json({sessions:await Ut()})});v.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await cr(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});v.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});if(!await fe(n))return t.status(404).json({error:"session not found"});try{await Ht(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});v.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});let r=await fe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await La(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});v.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ne(n))return t.status(400).json({error:"invalid session name"});try{let r=await xk(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});v.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=kl(n);if(!r)return t.json({running:!1});t.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var J_=10;v.get("/api/idle-guard",async(e,t)=>{try{t.json(Yh({tracks:await ie(),views:await lu(),queuedOf:n=>vo(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var jt=_o(),fH=sA({kind:jt}),kc=null,ft=()=>kc&&["auto","prompt","off"].includes(kc)?kc:fH,ag=null,pH=ec.has(jt),oI=async({to:e,from:t})=>!pH||ju(await tc())?!1:(xc.arm({to:e,from:t,kind:jt}),!0);async function mH(e){let t=ft();kc=e??null;let n=ft();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){xc.cancel();return}ag&&await oI(ag)}}var sI=(()=>{try{return dH(import.meta.url)("../package.json").version||null}catch{return null}})(),xc=iA({isBusy:async()=>Yh({tracks:await ie(),views:await lu(),queuedOf:e=>vo(e)}).busy,onStuck:({to:e,from:t})=>bn({current:t,latest:e,installKind:jt,policy:ft(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?bn({current:t,latest:e,installKind:jt,policy:ft(),status:"updating",reason:"auto"}):bn({current:t,latest:e,installKind:jt,policy:ft(),status:"failed",error:n?.reason})).catch(()=>{})});v.get("/api/node/self-update",async(e,t)=>{t.json({installKind:jt,policy:ft(),policySource:kc?"plane":"node",current:sI,status:await tc(),armed:xc.armed})});v.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Lu({to:n,kind:jt,reason:"manual"});if(r.ok)return xc.cancel(),bn({current:r.from,latest:n,installKind:jt,policy:ft(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function aI(){try{let e=await aA();e&&await bn({current:e.from,latest:e.to,installKind:e.kind||jt,policy:ft(),status:e.status,error:e.error,reason:e.reason}),await hh({current:sI,installKind:jt,policy:ft()})}catch{}}aI();setInterval(aI,3e4).unref();v.get("/api/tracks",async(e,t)=>{let n=(await ie()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>J_){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,J_).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Wi,agentAvailability:await vu(),tickets:await OA()})});v.get("/api/cost",async(e,t)=>{try{let n=await ie();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Cw(n);if(r.cursorUsage=lm(),e.query.format==="text")return t.type("text/plain").send(aE(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/roundup",async(e,t)=>{try{t.json(await lE(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/spend",async(e,t)=>{try{let n=await mE(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=lm(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=ai(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/caps",(e,t)=>{t.json(ai())});v.post("/api/usage/caps",async(e,t)=>{try{t.json(await Ol(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});v.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await SE(await ie(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await kg(e.get(An),{byToken:Ki,legacy:()=>ki(e.body?.session_id,e.body?.cwd)});if(n==="refused")return ga("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return ga("legacy-denied",{reason:"tokenless-denied",hook:e.body?.hook_event_name}),t.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&e.body?.session_id&&r.box?.sessionId&&e.body.session_id!==r.box.sessionId&&ga("mismatch",{track:r.track.id,box:r.box.name}),ga(n,{track:r?.track?.id,hook:e.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await QA(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&Fc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await Ki(e.get(An));if(!n?.track||!n?.box)return t.status(204).end();t.send((await tE(n.track,n.box)).join(`
|
|
693
|
+
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function h_(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${a2(n.host)}/`}function a2(e){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let t=String(e||"").replace(/:\d+$/,"").toLowerCase();if(!t||!t.includes(".")||/^[\d.]+$/.test(t))return"bostrat.globopi";let n=t.split(".");return yd(t)&&Um(n[0])?n.slice(1).join("."):t}Ks();eh();Iu();pm();me();import w_ from"node:os";import{randomUUID as ng}from"node:crypto";var i2=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||w_.hostname(),nodeName:e.BOSTRAT_NODE_NAME||w_.hostname()});var g_=32768,y_=120,b_=1e3,k_=new Set(["claude","codex","cursor"]),c2=new Set(["off","transition","nag"]),l2=[[0,59],[0,23],[1,31],[1,12],[0,6]];function u2(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=l2[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var an=e=>String(e??"").trim(),Go={name(e){let t=an(e);if(!t)throw new Error("a loop needs a name");if(t.length>y_)throw new Error(`name is too long (${t.length}); cap is ${y_}`);return t},schedule(e){let t=an(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!u2(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=an(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>g_)throw new Error(`prompt is too long (${t.length} chars); cap is ${g_}`);return t},expectation(e){let t=an(e)||null;if(t&&t.length>b_)throw new Error(`expectation is too long (${t.length}); cap is ${b_}`);return t},notify(e){let t=an(e)||"off";if(!c2.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=an(typeof t=="string"?t:t?.provider)||"claude";if(!k_.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...k_].join(", ")}`);let r=typeof t=="object"&&t&&an(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function d2(e,t){let n=e&&typeof e=="object"?e:{},r=Go.name(n.name),o=Go.schedule(n.schedule),s=Go.prompt(n.prompt),a=Go.expectation(n.expectation),i=Go.notify(n.notify),c=Go.assignment(n.assignment),l=an(t?.workspace?.repo)||an(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:f}=i2();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:f||null}}async function v_(e,t){let n=d2(t,e),r=ng();return await $({id:e.id,cwd:e.cwd},{type:"loop.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:r,...n}}),{proposalId:r,...n}}async function S_(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await F({id:e.id,cwd:e.cwd}),a=s.find(c=>c.type==="loop.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="loop.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await $({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("loopId required \u2014 the loop is created on the plane before the card is answered");let i=a.payload;return await $({id:e.id,cwd:e.cwd},{type:"loop.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:String(r),name:i.name,schedule:i.schedule,provider:i.assignment?.provider??"claude",repo:i.repo??null,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await $({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}var tg=["name","schedule","prompt","expectation","notify","assignment","enabled"],f2=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function T_(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function E_(e,t){let n=an(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function p2(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!n)throw new Error("a patch must be a JSON object of the fields to change");let r=Object.keys(n).filter(a=>!tg.includes(a));if(r.length){let a=r.filter(i=>["repo","nodeId","cwd","userId","owner","id","trackId"].includes(i));throw a.length?new Error(`a proposal can't change ${a.join(", ")} \u2014 retargeting a loop is a Loops-tab edit (scope D5)`):new Error(`unknown field(s) ${r.join(", ")} \u2014 patchable: ${tg.join(", ")}`)}let o={};for(let a of tg){if(!(a in n))continue;let i=Go[a](n[a]);(a==="assignment"?f2(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function A_(e,t,n){let r=T_(t),{patch:o,changed:s}=p2(n,r),a=ng(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await $({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var x_=600;async function __(e,t,n){let r=an(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>x_)throw new Error(`reason is too long (${r.length}); cap is ${x_}`);let o=T_(t),s=ng(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await $({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var I_=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await F({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function $_(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await I_(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await $(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await $(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function R_(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await I_(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await $(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await $(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}uu();me();ur();me();Fn();Iu();var O_=10,C_=200,m2=120,h2=24,N_=/^\d{4}-\d{2}-\d{2}$/;function rg(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var w2=(e=Date.now())=>new Date(e).toISOString().slice(0,10),pt=(e,t="i")=>new RegExp(e,t),g2=[{re:pt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:pt("\\bSPC\\b",""),reason:"business (SPC)"},{re:pt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:pt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:pt("\\bReplicas\\b",""),reason:"competitor research"},{re:pt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:pt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:pt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],y2=[pt("\\bSPC\\b",""),pt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),pt("competitor|cloudcli|\\bkiro\\b"),pt("\\bReplicas\\b",""),pt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),pt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),pt("(^|[^\\w&])#\\d+\\b"),pt("\\btrack/[\\w.-]+")];function b2({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of g2)if(o.test(n)||o.test(r))return s;return null}function M_(e){let t=String(e||"");for(let n of y2){let r=n.exec(t);if(r)return r[0].trim()}return null}function P_(e,t=Date.now()){if(!e)return t-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(e).trim());if(n)return t-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(e);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${e}" \u2014 use 24h, 7d, or an ISO date`)}var k2=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function x2(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=ji(e);if(t.length)throw new Error(`changelog entry appears to contain a secret VALUE (${t.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of e.split(`
|
|
694
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,m2)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${h2}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=k2.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let f=M_(i);if(f)throw new Error(`bullet ${r.length+1} mentions "${f}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>C_)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${C_}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=M_(n);if(o)throw new Error(`headline mentions "${o}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items; rewrite it and re-publish.`)}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>O_)throw new Error(`${r.length} bullets \u2014 over the ${O_}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function v2(e,{repos:t=rg()}={}){let n=new Set(t);return(await xo()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:b2({title:r.title,headRef:r.headRef})}))}async function D_(e,t){let n=await v2(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function B_(e,{md:t,day:n=null}){let r=x2(t),o=n==null||n===""?w2():String(n);if(!N_.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await $({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}async function j_(e,{day:t}){let n=String(t||"");if(!N_.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await $({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}Bs();Fn();Eo();me();Bs();it();Nr();Fn();gi();eu();Si();Bn();import{execFile as S2}from"node:child_process";import{promisify as T2}from"node:util";var sg=T2(S2),L_=3e4,E2=30*6e4,A2=L_,_2=10*6e4,I2=10080*6e4,$2=60*6e4,R2=8,O2=1440*6e4,C2=3,F_="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",og=60*6e4,M2=15*6e4,N2=9e4,P2=1440*6e4,kd=new Map,xd=new Map,D2=/could not resolve|not found|no pull requests?/i,B2=e=>D2.test(String(e?.message||e||""));function j2(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<og)}var U_={fast:A2,slow:_2,decay:$2};function H_(e,t){return e.armedAt&&t-e.armedAt<E2||j2(e,t)||W_(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>I2?"decay":"slow"}function L2(e,t){return U_[H_(e,t)]}function Tc(e,t,n,r){let o=e.lanes?.get(oe(t.repo,t.number))||{};return Ql({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function F2(e,t){Hn("watch");try{let{stdout:n}=await sg("gh",["pr","view",String(t),"--repo",e,"--json",F_],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await ln({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Rr(n)&&Or(n),n}}async function U2(e){Hn("watch");try{let{stdout:t}=await sg("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",F_],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await ln({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Rr(t)&&Or(t),t}}async function H2(){let e=new Map;for(let t of await le().catch(()=>[]))e.set(t.id,t);return e}async function W2(e,t){await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function q2(e,t){let n=await So(e);return n?Ps(n,t):null}async function K2(e,t,n){let r=await So(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ds(r,t,{method:n})}async function G2(e,t){let n=await So(e);if(!n)throw new Error(`no local checkout for ${e}`);return ki(n,t)}async function V2(e,t){await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function da(e,t,n,r,o){if(!await r.disarm(e.repo,e.number).catch(()=>null))return;let a=n.get(e.trackId);a&&await r.emitDisarmed(a,{number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo,reason:o}).catch(()=>{})}var z2=10*6e4;async function Y2(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>O2){await da(e,null,t,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(e.repo,e.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await da(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await da(e,o,t,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(e.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<z2)return;await da(e,o,t,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(e.repo,e.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(e.repo,e.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await da(e,o,t,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(e.repo,e.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await da(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await q_(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function fa(e,t,n){await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function J2(e,t){try{return await sg("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Sc(e,t,n,r=()=>!0){return(await F(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var pa=(e,t)=>({number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo});function W_(e,t,{grace:n=N2}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=P2}async function X2(e,t,n,r){let o=r.now??Date.now();if(!W_(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ve(s))return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??To)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await F(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??Ln)(e.repo,e.number).catch(()=>null),!1):await(r.claim??Ln)(e.repo,e.number).catch(()=>null)?(await fa(s,"opened",{...pa(e,t),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${e.repo}#${e.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function Z2(e,t,n,r){let o=r.now??Date.now(),s=t.headRefOid||null,a=n.get(e.trackId)||null;if(s&&(e.ciFix?.headSha&&s!==e.ciFix.headSha&&await Kl(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Vl(e.repo,e.number,{headSha:s}).catch(()=>{})),s&&s!==e.lastHeadSha){let i=!e.lastHeadSha,c=!!(e.ciFix||e.reconcile||e.ciFixAwait||e.reconcileAwait),l=!!e.baseFoldAt&&o-e.baseFoldAt<M2;await Cx(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Ox(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Sc(a,"pushed",e.number,f=>f.payload?.sha===s)&&await fa(a,"pushed",{...pa(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>og)await Gl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await Gl(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Tc(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Sc(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await fa(a,"ci-fixed",{...pa(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Gl(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>og)await zl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await zl(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Tc(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Sc(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await fa(a,"reconciled",{...pa(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await zl(e.repo,e.number).catch(()=>{})}}}}async function Q2(e,t,n,r){let o=Tc(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Sc(s,"closed",e.number)&&await fa(s,"closed",{...pa(e,t),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(e.repo,e.number,"closed").catch(()=>{}),await r.mark(e.repo,e.number,"closed").catch(()=>{})}async function eH(e,t,n){Tc(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Sc(r,"lost",e.number)&&await fa(r,"lost",pa(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function q_(e,t,n,r,o={}){let s=Tc(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await F(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function tH(e={}){let{now:t=Date.now(),auth:n=Zp,view:r=F2,list:o=U2,links:s=Dx,trackMap:a=H2,emitMerged:i=W2,mark:c=Ir,archiveIfArmed:l=qn,notifyIfArmed:u=Kn,notifyIfResolved:f=Lu,followupsIfMerged:d=Bo,batchThreshold:p=C2,detail:h=q2,merge:w=K2,updateBranch:g=G2,lookup:k=tt,disarm:S=wi,emitDisarmed:E=V2,localHasCommit:R=J2,claim:B=Ln,midTurn:T=To}=e;if(!(await n().catch(()=>null))?.authenticated||Xl()>0)return 0;let O=await s().catch(()=>[]),I=av(O),U=O.filter(W=>t-(kd.get(oe(W.repo,W.number))||0)>=L2(W,t));if(!U.length)return 0;let H=Date.now(),ae=new Map,X=new Map;for(let W of[...U].sort(xi)){let be=H_(W,t);ae.set(oe(W.repo,W.number),{lane:be,intervalMs:U_[be]}),kd.set(oe(W.repo,W.number),t),X.has(W.repo)||X.set(W.repo,[]),X.get(W.repo).push(W)}let C=await a().catch(()=>new Map),Q={now:t,lanes:ae,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,followupsIfMerged:d,detail:h,merge:w,updateBranch:g,lookup:k,disarm:S,emitDisarmed:E,localHasCommit:R,claim:B,midTurn:T};for(let[W,be]of X){let se,pe=new Set;if(be.length>p)se=await o(W).catch(()=>null);else{se=new Map;for(let ie of be)try{let L=await r(W,ie.number);L?.number!=null&&se.set(Number(L.number),L)}catch(L){B2(L)&&pe.add(oe(ie.repo,ie.number))}}if(se)for(let ie of be){let L=oe(ie.repo,ie.number),ce=se.get(Number(ie.number));if(!ce){if(pe.has(L)){let ue=(xd.get(L)||0)+1;xd.set(L,ue),ue>=R2&&(await eH(ie,C,Q),xd.delete(L),kd.delete(L))}continue}if(xd.delete(L),ce.state==="MERGED"||ce.state==="CLOSED")ce.state==="MERGED"?await q_(ie,ce,C,Q):await Q2(ie,ce,C,Q),kd.delete(L);else{await X2(ie,ce,C,Q).catch(()=>{}),await Z2(ie,ce,C,Q).catch(()=>{});let ue=!ie.trackId||I.get(ie.trackId)===L;ie.autoMerge&&ue&&await Y2(ie,C,Q).catch(()=>{})}}}let ve={};for(let{lane:W}of ae.values())ve[W]=(ve[W]||0)+1;return console.log(`[pr-watch] probed ${U.length} link(s) (${Object.entries(ve).map(([W,be])=>`${W} ${be}`).join(", ")}) in ${Date.now()-H}ms`),U.length}function K_(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await tH()}catch{}finally{e=!1}}},L_)}me();Fn();ar();Nr();Bn();function G_(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function nH(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var rH=new Set(["merged","closed","lost"]);function oH(e,t){if(!e?.blocked)return null;let n=G_(t);if(e.blocked==="red")return t.checks.failed===0&&t.checks.total>0?{change:"ci-fixed",reason:"red"}:null;if(e.blocked==="conflict")return t.mergeable==="MERGEABLE"&&n!=="red"?{change:"reconciled",reason:"conflict"}:null;if(e.blocked==="behind"){let r=t.headSha??null;return n===null&&r&&e.sha&&r!==e.sha?{change:"reconciled",reason:"behind"}:null}return null}function sH(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function aH(e,t,n,r,o){return(await F(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function iH(e,t,n){await $({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function V_(e,t={}){let{now:n=Date.now(),slugOf:r=Se,lookup:o=tt,note:s=Mx,tracks:a=async()=>(await le()).filter(f=>!Ve(f)),asked:i=aH,emitChange:c=iH}=t,l=[],u=null;for(let f of e??[]){if(!f?.prs?.length)continue;let d=await r(f.path).catch(()=>null);if(d)for(let p of f.prs)try{if(!nH(p))continue;let h=await o(d,p.number).catch(()=>null);if(!h?.trackId||rH.has(h.state))continue;let{prev:w}=await s(d,p.number,{blocked:G_(p),sha:p.headSha??null,at:n});if(!w)continue;let g=oH(w,p);if(!g||sH(h,g.change))continue;u??=new Map((await a()).map(R=>[R.id,R]));let k=u.get(h.trackId);if(!k)continue;let S=p.headSha??null;if(await i(k,g.change,p.number,S,w.at||0))continue;let E={number:Number(p.number),url:p.url||h.url,title:p.title||h.title,branch:p.headRefName||h.headRef,repo:d,sha:S,delegated:!1,reason:g.reason};g.change==="ci-fixed"?E.passed=p.checks.passed:E.base=p.baseRefName||null,await c(k,g.change,E),l.push({change:g.change,number:p.number,reason:g.reason})}catch{}}return l}lt();me();Nr();Bn();import{randomUUID as cH}from"node:crypto";var lH=2e4,z_=3,ma=new Map,ag=new Set;async function Y_(e,t,n){let r=await n(e).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===t?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function uH(e={}){let{sessions:t=null,tracks:n=null,append:r=$,read:o=F,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await zt(),i=n??await le()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ve(u)||!u.leadBox)continue;let f=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let p=(ma.get(f.name)||0)+1;ma.set(f.name,p),p===z_&&(ag.add(f.name),await Y_(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:cH(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(ma.get(f.name)||0)>=z_;if(ma.delete(f.name),p||!ag.has(f.name)){ag.add(f.name);let h=await Y_(u,f.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ma.keys()])l.has(u)||ma.delete(u)}var Ec=null,ig=!1;function J_(){return Ec?()=>{}:(Ec=setInterval(()=>{ig||(ig=!0,uH().catch(()=>{}).finally(()=>{ig=!1}))},lH),Ec.unref?.(),()=>{clearInterval(Ec),Ec=null})}bo();qm();me();Bs();Eo();bo();import{randomUUID as dH}from"node:crypto";async function fH(){let e=await Promise.resolve().then(()=>(Nr(),vE));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var cg={listRepoPrs:hn,readEvents:F,appendEvent:$,sayDeps:fH,currentBranch:Cr};async function pH(e,t=cg,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Cr)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function lg(e,t,{actions:n,notify:r=!1}={},o=cg){let s=t?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await pH(e,o,{branch:s.branch});if(!a)return null;let i={id:e.id,cwd:e.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=dH();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:c,srcOfferId:s.offerId??null,branch:s.branch,base:s.base??null,pr:a,actions:n,...r?{notify:!0}:{},offeredRebase:Array.isArray(s.offered)&&s.offered.includes("rebase")}}),{offerId:c,pr:a}}async function ug(e,{offerId:t,choice:n}={},r=cg){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=Ar.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Os(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,xx(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import rr from"node:fs/promises";import ha from"node:path";import rI from"node:os";import X_ from"node:fs/promises";import vd from"node:path";import{execFile as mH}from"node:child_process";import{promisify as hH}from"node:util";import{fileURLToPath as wH}from"node:url";var gH=hH(mH),Q_=vd.dirname(wH(import.meta.url)),yH=vd.resolve(Q_,"..","..","client","dist"),bH="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",kH=new Map([...bH].map((e,t)=>[e,t]));function xH(e){let t=[],n=0,r=0;for(let o of e){let s=kH.get(o);if(s===void 0)return t;n+=(s&31)<<r,s&32?r+=5:(t.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return t}function vH(e){let t=[],n=0,r=0,o=0;for(let s of e.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=xH(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}t.push(a)}return t}function SH(e,t){if(!e||e.length===0||e[0][0]>t)return null;let n=0,r=e.length-1;for(;n<r;){let o=n+r+1>>1;e[o][0]<=t?n=o:r=o-1}return e[n]}var Z_=new Map;async function TH(e){let t;try{t=await X_.stat(e)}catch{return null}let n=Z_.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await X_.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:vH(r.mappings||"")};return Z_.set(e,o),o}catch{return null}}var dg=null;function EH(){return dg||(dg=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):gH("git",["rev-parse","--short","HEAD"],{cwd:vd.resolve(Q_,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),dg}var AH=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function _H(e,{distDir:t=yH}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(AH)){let[a,i,c,l]=s,u=await TH(vd.join(t,"assets",`${i}.map`)),f=u?SH(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function eI(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await EH();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await _H(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var IH=()=>process.env.BOSTRAT_STATE_DIR||ha.join(process.env.HOME||rI.tmpdir(),".bostrat"),Qr=()=>ha.join(IH(),"telemetry"),oI=()=>ha.join(Qr(),".relay-offsets.json"),$H=50,RH=14,OH=3e4,tI=500,jt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function CH(e,t){if(!e||typeof e!="object")return null;let n=jt(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:jt(e.anonId,64),sessionId:jt(e.sessionId,64),userId:jt(e.userId,64),release:jt(e.release,64),appVersion:jt(e.appVersion,32),route:jt(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:jt(e.error.kind,256),message:jt(e.error.message,8192),stack:jt(e.error.stack,32768),fingerprint:jt(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:jt(s?.n,64),r:jt(s?.r,128)}))),o}var sI=e=>new Date(e).toISOString().slice(0,10),nI=0;async function aI(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,$H):[]).map(s=>CH(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await eI(s);await rr.mkdir(Qr(),{recursive:!0});let o=ha.join(Qr(),`${sI(t)}.ndjson`);return await rr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
695
|
+
`).join(""),"utf8"),t-nI>6*3600*1e3&&(nI=t,MH(t).catch(()=>{})),r.length}async function MH(e=Date.now()){let t=sI(e-RH*24*3600*1e3),n=[];try{n=await rr.readdir(Qr())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<t);if(!r.length)return;for(let s of r)await rr.rm(ha.join(Qr(),s),{force:!0});let o=await cI();for(let s of r)delete o[s];await lI(o)}var Sd=new Map;function iI(e,t=Date.now()){let n=e||"unknown",r=Sd.get(n);return r||(Sd.size>512&&Sd.clear(),r={tokens:40,at:t},Sd.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 cI(){try{return JSON.parse(await rr.readFile(oI(),"utf8"))||{}}catch{return{}}}async function lI(e){await rr.mkdir(Qr(),{recursive:!0}),await rr.writeFile(oI(),JSON.stringify(e),"utf8")}function NH(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||rI.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 PH({fetchImpl:e=fetch}={}){let t=await zE();if(!t)return{sent:0,disabled:!0};let n;try{n=(await rr.readdir(Qr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await cI(),o=0;for(let s of n){let a=ha.join(Qr(),s),i=r[s]||0,c;try{let d=await rr.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
696
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=tI){let p=f.slice(d,d+tI).map(w=>NH(w)),h=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!h||!h.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await lI(r)}return{sent:o,disabled:!1}}var fg=null,pg=!1;function uI(){fg||(fg=setInterval(async()=>{if(!pg){pg=!0;try{await PH()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{pg=!1}}},OH),fg.unref?.())}import Ed from"node:fs/promises";import hI from"node:path";import DH from"node:os";var wI=()=>process.env.BOSTRAT_STATE_DIR||hI.join(process.env.HOME||DH.tmpdir(),".bostrat"),gI=()=>hI.join(wI(),"dictation-glossary.json"),BH=100,jH=500,LH=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Ad=null,mg=null,hg=Promise.resolve();function dI(){let e={},t=Date.now();for(let n of LH)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function yI(){return Ad||(mg??=Ed.readFile(gI(),"utf8").then(e=>JSON.parse(e)?.terms??dI()).catch(()=>dI()).then(e=>Ad=e).finally(()=>mg=null),mg)}async function fI(){let e=JSON.stringify({terms:Ad},null,2),t=gI(),n=`${t}.${process.pid}.tmp`;await Ed.mkdir(wI(),{recursive:!0}),await Ed.writeFile(n,e),await Ed.rename(n,t)}function FH(){return hg=hg.then(fI,fI),hg.catch(()=>{})}async function wg(){return Object.values(await yI()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var UH=e=>Math.max(1,Math.ceil(e.length/4));async function HH(){let e=[],t=0;for(let{term:n}of await wg()){let r=UH(n);if(!(t+r>jH)&&(e.push(n),t+=r,e.length>=BH))break}return e}async function WH(e,t){let n=await yI(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var pI=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Td=e=>e.toLowerCase();function mI(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function bI(e,t){let n=pI(e),r=pI(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let f=s-1;f>=0;f--)a[u][f]=Td(n[u])===Td(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)Td(n[c])===Td(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(mI(r[l])&&i.add(r[l]),l++);for(;l<s;l++)mI(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await WH(u,"learned");return await FH(),[...i]}var Ac=null;function qH(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return e===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${e}: ${t.slice(0,300)}`}}async function kI(){if(!Ac)return null;let e=await uc();if(!e||e.apiKey.slice(-6)!==Ac.keyTail)return null;let{keyTail:t,...n}=Ac;return n}async function xI(e,t){let n=await uc();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await HH())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":t||"application/octet-stream"},body:e});if(!o.ok){let i=await o.text().catch(()=>""),c=qH(o.status,i);c.persistent&&(Ac={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return Ac=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}me();var gg={"gpt-realtime-2.1":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime":{textIn:4,textInCached:.4,textOut:16,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2.1-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20},"gpt-realtime-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20}};function KH(e){if(!e)return null;let t=String(e).trim();if(gg[t])return t;let n=null;for(let r of Object.keys(gg))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function GH(e,t={}){let n=gg[KH(e)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(t[o])||0)*n[o];return r/1e6}function VH(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.error?.code||n?.error?.type||"";return e===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${e}): ${t.slice(0,300)}`}}var _c=null;async function zH(){if(!_c)return null;let e=await rd();if(!e||e.apiKey.slice(-6)!==_c.keyTail)return null;let{keyTail:t,...n}=_c;return n}async function YH(e){let t=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:e.model,audio:{output:{voice:e.voice}}}})});if(!t.ok){let o=await t.text().catch(()=>""),s=VH(t.status,o);s.persistent&&(_c={code:s.code,message:s.message,at:Date.now(),keyTail:e.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}_c=null;let n=await t.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var JH={openai:YH};async function vI(){let e=await rd();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await zH()}:{configured:!1}}async function SI(){let e=await rd();if(!e){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let t=await JH[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function TI(e,{model:t,usage:n={},seconds:r}={}){let o=c=>Math.max(0,Math.round(Number(c)||0)),s={textIn:o(n.textIn),textInCached:o(n.textInCached),textOut:o(n.textOut),audioIn:o(n.audioIn),audioInCached:o(n.audioInCached),audioOut:o(n.audioOut)},a=GH(t,s),i=await $(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}Bn();var OI=xe.dirname(ZH(import.meta.url)),EI=Number(process.env.PORT)||3362,Id=fw(process.env),CI=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,wa=s_(process.env.BOSTRAT_ALLOW_IPS),vg=a_(process.env.BOSTRAT_VIEW_FRONT_CIDRS,bd),MI=c_(),NI=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",v=Gt();v.disable("x-powered-by");v.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),yd(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});v.use((e,t,n)=>{if(vc(e)||wa.allowed(wa.clientIp(e))||vg.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});v.use(p_);v.use(d_({token:MI,enforce:NI}));v.use("/api/mocks",Gt.json({limit:"1mb"}));v.use("/api/mocks",Gt.raw({type:"image/png",limit:"24mb"}));v.use("/api/mocks",Gt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));v.use("/api/runbooks",Gt.json({limit:"1mb"}));v.use("/api/tracks/adopt",Gt.json({limit:"1mb"}));v.use("/api/telemetry",Gt.json({limit:"256kb"}));v.use("/api/hook",Gt.json({limit:"2mb"}));v.use("/api/dictation",Gt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));v.use("/api/tracks/:id/upload",Gt.raw({type:()=>!0,limit:"25mb"}));v.use(Gt.json({limit:"64kb"}));v.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:$t,hostTmuxAlive:await fr(),autoTriggerPct:CI,plane:ga?ga.status():null})});v.get("/api/sessions",async(e,t)=>{t.json({sessions:await zt()})});v.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await pr(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});v.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Pe(n))return t.status(400).json({error:"invalid session name"});if(!await ge(n))return t.status(404).json({error:"session not found"});try{await Yt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});v.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Pe(n))return t.status(400).json({error:"invalid session name"});let r=await ge(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Ka(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});v.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Pe(n))return t.status(400).json({error:"invalid session name"});try{let r=await Bk(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});v.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=Il(n);if(!r)return t.json({running:!1});t.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var AI=10;v.get("/api/idle-guard",async(e,t)=>{try{t.json(dw({tracks:await le(),views:await ku(),queuedOf:n=>Mo(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Kt=Lo(),rW=DE({kind:Kt}),Ic=null,kt=()=>Ic&&["auto","prompt","off"].includes(Ic)?Ic:rW,xg=null,oW=cc.has(Kt),PI=async({to:e,from:t})=>!oW||Ju(await lc())?!1:($c.arm({to:e,from:t,kind:Kt}),!0);async function sW(e){let t=kt();Ic=e??null;let n=kt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){$c.cancel();return}xg&&await PI(xg)}}var DI=(()=>{try{return nW(import.meta.url)("../package.json").version||null}catch{return null}})(),$c=jE({isBusy:async()=>dw({tracks:await le(),views:await ku(),queuedOf:e=>Mo(e)}).busy,onStuck:({to:e,from:t})=>An({current:t,latest:e,installKind:Kt,policy:kt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?An({current:t,latest:e,installKind:Kt,policy:kt(),status:"updating",reason:"auto"}):An({current:t,latest:e,installKind:Kt,policy:kt(),status:"failed",error:n?.reason})).catch(()=>{})});v.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Kt,policy:kt(),policySource:Ic?"plane":"node",current:DI,status:await lc(),armed:$c.armed})});v.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Xu({to:n,kind:Kt,reason:"manual"});if(r.ok)return $c.cancel(),An({current:r.from,latest:n,installKind:Kt,policy:kt(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function BI(){try{let e=await BE();e&&await An({current:e.from,latest:e.to,installKind:e.kind||Kt,policy:kt(),status:e.status,error:e.error,reason:e.reason}),await Rh({current:DI,installKind:Kt,policy:kt()})}catch{}}BI();setInterval(BI,3e4).unref();v.get("/api/tracks",async(e,t)=>{let n=(await le()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>AI){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,AI).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Xi,agentAvailability:await Mu(),tickets:await cA()})});v.get("/api/cost",async(e,t)=>{try{let n=await le();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Vw(n);if(r.cursorUsage=Tm(),e.query.format==="text")return t.type("text/plain").send(BA(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/roundup",async(e,t)=>{try{t.json(await FA(await le(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/spend",async(e,t)=>{try{let n=await KA(await le(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Tm(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=fi(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/caps",(e,t)=>{t.json(fi())});v.post("/api/usage/caps",async(e,t)=>{try{t.json(await Ll(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});v.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await e_(await le(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Dg(e.get(Rn),{byToken:Qi,legacy:()=>Ii(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Sa("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Sa("legacy-denied",{reason:"tokenless-denied",hook:e.body?.hook_event_name}),t.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&e.body?.session_id&&r.box?.sessionId&&e.body.session_id!==r.box.sessionId&&Sa("mismatch",{track:r.track.id,box:r.box.name}),Sa(n,{track:r?.track?.id,hook:e.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await RA(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&zc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await Qi(e.get(Rn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await CA(n.track,n.box)).join(`
|
|
658
697
|
`)+`
|
|
659
|
-
`)}catch{t.status(204).end()}});v.post("/api/git/committed",async(e,t)=>{try{let n=await Ki(e.get(An));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?ke.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?ke.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+ke.sep))?s:o;nE(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await rE(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});v.get("/api/usage/rework",async(e,t)=>{try{t.json(await hE(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/telemetry",async(e,t)=>{try{if(!R_(la.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await $_(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/repos",async(e,t)=>{try{let n=await En(),r=new Set((await Promise.all(n.map(a=>xe(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await xa()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/github/token",async(e,t)=>{try{t.json(await Oc(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/github/status",(e,t)=>{t.json({configured:Rc(),calls:$x(),latency:Mx()})});v.post("/api/github/refresh",(e,t)=>{Rg(),t.json({ok:!0})});v.get("/api/gmail/search",async(e,t)=>{try{let n=await AE(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await EE(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function cg(e,t,n){let r=e.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>t&&o?.base===t||n&&o?.repo===n):!r?.base||!t||r.base===t||n&&r.repo===n}async function gd({repo:e=null,number:t=null,branch:n=null}){let r=e?await xe(e):null;if(r&&t!=null){let s=await Qe(r,t),a=s&&await oe(s.trackId);if(a)return a}if(!n)return null;let o=await ie();return Xl(o,n,s=>cg(s,e,r))}async function iI({repo:e=null,number:t=null,branch:n=null}){let r=await gd({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!qe(r))return r;if(!n)return null;let o=e?await xe(e).catch(()=>null):null,s=await ie();return Xl(s,n,a=>!qe(a)&&cg(a,e,o))}async function en(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await gd({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await xe(s).catch(()=>null):null);await O({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function hH(e){let t=(await ie()).filter(n=>!qe(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await xe(n.path);if(r)for(let o of n.prs){if(await Qe(r,o.number))continue;let s=await Xl(t,o.headRefName,a=>cg(a,n.path,r));s&&await St({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}v.get("/api/git/prs",async(e,t)=>{try{let n=await Fx();await hH(n.repos).catch(()=>{}),await w_(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{};try{if(s){let w=o||"merge";if(!pi.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let x=await xe((await jn(n)).path);if(!x)throw new Error("repo has no GitHub remote");let T=await gd({repo:n,number:y,branch:l}).catch(()=>null);if(!T)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await mi(n,y),await St({repo:x,number:y,trackId:T.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ci(x,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await fo(x,y,i===!0),await li(x,y,c===!0&&i!==!0),un(),await en("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:T,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}let f=await Is(n,r,{method:o||"squash"});await en("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await xe(n).catch(()=>null),m=!1;if(p&&(i===!0&&await fo(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await li(p,f.number,!0).catch(()=>{}),await Fn(p,f.number).catch(()=>{}),m=await Ln(p,f.number).catch(()=>!1),await vr(p,f.number,"merged").catch(()=>{})),i===!0&&!m){let w=await gd({repo:n,number:f.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Ao(w.id).catch(()=>{})}t.json(f)}catch(f){let p=f?.message||String(f),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(m).json({error:p})}});v.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await xe((await jn(n)).path).catch(()=>null),l=c?await ui(c,i).catch(()=>null):null;try{await Gp(n,i)}catch(u){if(!l)throw u}un(),await en("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await xe(n).catch(()=>null),s=await Kx(n,{method:r||"merge",onMerged:a=>{en("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Fn(o,a.number).catch(()=>{}).then(()=>Ln(o,a.number)).catch(()=>{}).then(()=>vr(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});v.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:qx()})});v.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!pi.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await oe(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await po()).filter(f=>f.trackId===c.id&&f.state!=="merged"&&f.state!=="closed").sort(hi);if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let f of l)await fo(f.repo,f.number,o===!0).catch(()=>{}),await li(f.repo,f.number,s===!0&&o!==!0).catch(()=>{});let u=[],d=[];for(let f of l){let p={repo:f.repo,number:f.number,ok:!0,action:"armed"};u.push(p);try{let m=await ho(f.repo);if(!m)throw new Error(`no local checkout for ${f.repo}`);let w=await _s(m,f.number);d.push({r:p,l:f,repoPath:m,pr:w,plan:Ux(w,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:f,l:p,repoPath:m,pr:w,plan:y}of Hx(d))try{if(y.action==="skip")f.action="skipped",f.reason=y.reason;else if(y.action==="merge")await Is(m,p.number,{method:i}),await en("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await Fn(p.repo,p.number).catch(()=>{}),await Ln(p.repo,p.number).catch(()=>{}),await vr(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(y.action==="update-arm"&&(await mi(m,p.number).catch(()=>{}),f.action="updated-armed"),!await ci(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await en("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c,...f.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(x){f.ok=!1,f.action="failed",f.reason=x?.message||String(x)}if(un(),!u.some(f=>f.ok)){if(o===!0)for(let f of l)await fo(f.repo,f.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let f of l)await li(f.repo,f.number,!1).catch(()=>{});return t.status(502).json({error:u.map(f=>`${f.repo}#${f.number}: ${f.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await oe(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await po()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ui(a.repo,a.number).catch(()=>null),c=await ho(a.repo).catch(()=>null);c&&await Gp(c,a.number).catch(()=>{}),i&&(await en("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}un(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await oe(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Et(i.workspace),d,f=Array.isArray(a)?a.filter(Boolean):[];if(f.length)d=f.map(w=>{let y=u.find(x=>x.name===w||x.repo===w||x.base===w||x.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(d=await Ms(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of d)try{let y=await zp(w.dir,{title:r,body:o,base:s}),x=w.repo||await xe(w.dir).catch(()=>null);x&&y.number&&await St({repo:x,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await en("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:x??w.name}}),p.push({repo:w.name,slug:x??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?t.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):t.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await zp(i.cwd,{title:r,body:o,base:s}),l=await xe(i.cwd).catch(()=>null);l&&c.number&&await St({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await en("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});v.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ms(n),o=new Map(r.map(a=>[a.base,a])),s=Et(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Eh(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await _h(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});v.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await _s(n,r),s=await xe(n).catch(()=>null),a=await iI({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Et(a.workspace).find(x=>x.base===n||s&&x.repo===s)??null,c=a.workspace?.kind==="multi",l=await Gx(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ke.join("ci",i.name):"ci",d=ke.join(a.cwd,".bostrat",u),f=ke.join(".bostrat",u,`pr-${o.number}-fail.log`);await at.promises.mkdir(d,{recursive:!0}),await at.promises.writeFile(ke.join(a.cwd,f),l.map(x=>`\u2550\u2550\u2550\u2550 ${x.name}${x.link?` \xB7 ${x.link}`:""} \u2550\u2550\u2550\u2550
|
|
660
|
-
${
|
|
698
|
+
`)}catch{t.status(204).end()}});v.post("/api/git/committed",async(e,t)=>{try{let n=await Qi(e.get(Rn));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?xe.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?xe.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+xe.sep))?s:o;MA(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await NA(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});v.get("/api/usage/rework",async(e,t)=>{try{t.json(await GA(await le(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/telemetry",async(e,t)=>{try{if(!iI(wa.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await aI(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/repos",async(e,t)=>{try{let n=await On(),r=new Set((await Promise.all(n.map(a=>Se(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await _a()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/github/token",async(e,t)=>{try{t.json(await Lc(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/github/status",(e,t)=>{t.json({configured:jc(),calls:Gx(),latency:Jx()})});v.post("/api/github/refresh",(e,t)=>{Gg(),t.json({ok:!0})});v.get("/api/gmail/search",async(e,t)=>{try{let n=await n_(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await r_(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Sg(e,t,n){let r=e.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>t&&o?.base===t||n&&o?.repo===n):!r?.base||!t||r.base===t||n&&r.repo===n}async function $d({repo:e=null,number:t=null,branch:n=null}){let r=e?await Se(e):null;if(r&&t!=null){let s=await tt(r,t),a=s&&await te(s.trackId);if(a)return a}if(!n)return null;let o=await le();return au(o,n,s=>Sg(s,e,r))}async function jI({repo:e=null,number:t=null,branch:n=null}){let r=await $d({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ve(r))return r;if(!n)return null;let o=e?await Se(e).catch(()=>null):null,s=await le();return au(s,n,a=>!Ve(a)&&Sg(a,e,o))}async function cn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await $d({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Se(s).catch(()=>null):null);await $({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function aW(e){let t=(await le()).filter(n=>!Ve(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Se(n.path);if(r)for(let o of n.prs){if(await tt(r,o.number))continue;let s=await au(t,o.headRefName,a=>Sg(a,n.path,r));s&&await Ot({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}v.get("/api/git/prs",async(e,t)=>{try{let n=await rv();await aW(n.repos).catch(()=>{}),await V_(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=e.body||{};try{if(s){let w=o||"merge";if(!bi.has(w))throw new Error(`invalid merge method: ${w}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let k=await Se((await Wn(n)).path);if(!k)throw new Error("repo has no GitHub remote");let S=await $d({repo:n,number:g,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ki(n,g),await Ot({repo:k,number:g,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await mi(k,g,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await ko(k,g,i===!0),await hi(k,g,c===!0&&i!==!0),gn(),await cn("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:g,auto:!0})}let d=await Ds(n,r,{method:o||"squash"});await cn("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await Se(n).catch(()=>null),h=!1;if(p&&(i===!0&&await ko(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await hi(p,d.number,!0).catch(()=>{}),await Kn(p,d.number).catch(()=>{}),await Bo(p,d.number).catch(()=>{}),h=await qn(p,d.number).catch(()=>!1),await Ir(p,d.number,"merged").catch(()=>{})),i===!0&&!h){let w=await $d({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Do(w.id).catch(()=>{})}t.json(d)}catch(d){let p=d?.message||String(d),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(h).json({error:p})}});v.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Se((await Wn(n)).path).catch(()=>null),l=c?await wi(c,i).catch(()=>null):null;try{await tm(n,i)}catch(u){if(!l)throw u}gn(),await cn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Se(n).catch(()=>null),s=await cv(n,{method:r||"merge",onMerged:a=>{cn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Kn(o,a.number).catch(()=>{}).then(()=>Bo(o,a.number)).catch(()=>{}).then(()=>qn(o,a.number)).catch(()=>{}).then(()=>Ir(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});v.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:iv()})});v.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!bi.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await te(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await xo()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed").sort(xi);if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await ko(d.repo,d.number,o===!0).catch(()=>{}),await hi(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let p={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(p);try{let h=await So(d.repo);if(!h)throw new Error(`no local checkout for ${d.repo}`);let w=await Ps(h,d.number);f.push({r:p,l:d,repoPath:h,pr:w,plan:ov(w,{update:r!==!1})})}catch(h){p.ok=!1,p.action="failed",p.reason=h?.message||String(h)}}for(let{r:d,l:p,repoPath:h,pr:w,plan:g}of sv(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await Ds(h,p.number,{method:i}),await cn("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await Kn(p.repo,p.number).catch(()=>{}),await Bo(p.repo,p.number).catch(()=>{}),await qn(p.repo,p.number).catch(()=>{}),await Ir(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await ki(h,p.number).catch(()=>{}),d.action="updated-armed"),!await mi(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await cn("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){d.ok=!1,d.action="failed",d.reason=k?.message||String(k)}if(gn(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await ko(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await hi(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});v.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await te(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await xo()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await wi(a.repo,a.number).catch(()=>null),c=await So(a.repo).catch(()=>null);c&&await tm(c,a.number).catch(()=>{}),i&&(await cn("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}gn(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await te(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Dt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let g=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!g)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return g});else if(f=await _o(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let g=await rm(w.dir,{title:r,body:o,base:s}),k=w.repo||await Se(w.dir).catch(()=>null);k&&g.number&&await Ot({repo:k,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await cn("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??w.name}}),p.push({repo:w.name,slug:k??null,ok:!0,url:g.url,number:g.number})}catch(g){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:g?.message||String(g)})}let h=p.filter(w=>w.ok);return h.length?t.json({ok:!0,multi:!0,results:p,url:h[0].url,number:h[0].number}):t.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await rm(i.cwd,{title:r,body:o,base:s}),l=await Se(i.cwd).catch(()=>null);l&&c.number&&await Ot({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await cn("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});v.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await _o(n),o=new Map(r.map(a=>[a.base,a])),s=Dt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Uh(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});v.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Hh(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});v.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Ps(n,r),s=await Se(n).catch(()=>null),a=await jI({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Dt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await lv(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?xe.join("ci",i.name):"ci",f=xe.join(a.cwd,".bostrat",u),d=xe.join(".bostrat",u,`pr-${o.number}-fail.log`);await mt.promises.mkdir(f,{recursive:!0}),await mt.promises.writeFile(xe.join(a.cwd,d),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
699
|
+
${k.log??"(no fetchable log \u2014 see link)"}
|
|
661
700
|
`).join(`
|
|
662
|
-
`));let p=l.map(x=>x.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await Bt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${f} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await St({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await fx(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await en("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(x=>x.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(x=>x.name),log:f})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await _s(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await xe(n).catch(()=>null),a=await iI({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Et(a.workspace).find(d=>d.base===n||s&&d.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await Bt(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),u=s;u&&(await St({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await px(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await en("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=xs.filter(d=>e.body.actions.includes(d)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await F(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await zw(a,c,{actions:o,notify:s});if(d)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await Bt(a.id,vs(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await O({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&xh(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Jw(o,{offerId:n,choice:r});t.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});var vc=e=>t=>({...t,url:jE(t,e.headers)});v.get("/api/views",async(e,t)=>{try{let n=await lu({trackId:e.query.track||void 0});t.json({views:n.map(vc(e)),maxLive:Rr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/views/:id",async(e,t)=>{let n=await Xv(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:vc(e)(n),session:Ps(n.slug)})});v.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await oe(n):null;if(!i&&s&&(i=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ke.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await _m({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});t.json({ok:!0,view:vc(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});v.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:vc(e)(await Zv(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:vc(e)(await Qv(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/views/:id",async(e,t)=>{try{t.json(await eS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var wH=[["linear",Io,cw],["jira",qu,aw]],rg=new Map;v.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await sw(),r={};for(let[o,s,a]of wH){let i=await s();if(!i){rg.delete(o),r[o]={configured:!1};continue}let c=rg.get(o);if(!c||Date.now()-c.at>300*1e3){let l;try{l={configured:!0,ok:!0,...await a(i)}}catch(u){l={configured:!0,ok:!1,error:u?.message||String(u)}}c={at:Date.now(),body:l},rg.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await sw()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await wA(o),i=n?await oe(n):null;!i&&r&&(i=(await ie()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ke.sep)))||null),i&&await O({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var Po=async(e,t)=>{let n=e?await oe(e):null;return!n&&t&&(n=(await ie()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+ke.sep)))||null),n};v.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await ew()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});v.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Po(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await Xs(o);await Fu(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});v.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Po(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Fu(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var cI=async(e,t,n)=>{if(e)return{ref:e,track:await Po(t,n)};let r=await Po(t,n),o=r?await nw(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};v.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await cI(o,n,r),l=await cA({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});v.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await cI(e.query.circuit,e.query.track,e.query.cwd);t.json(await tw(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});v.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Po(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await dA(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});v.get("/api/decisions",async(e,t)=>{try{let n=await Po(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await fA(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});v.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await Po(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await nw(a.id);if(!i)return t.status(400).json({error:"this Track isn't in a circuit \u2014 link one first (bostrat-circuit link \u2026)"});let c=await lA(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});v.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ke.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await ea(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await oe(r):null;if(!o&&n&&(o=(await ie()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ke.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await CA(),ticket:await xn(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});v.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await xn(e.params.id)})});v.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await oe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ea(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});v.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await kw(e.params.id)})});var yd=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;v.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await DA(e.params.id))}catch(n){let r=n?.message||String(n);t.status(yd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await xw(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(yd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await BA(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(yd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await jA(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(yd(s)).json({error:s})}});v.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Bh(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});v.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ke.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await FA(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await LA()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});v.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await UA(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});v.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await $i({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/mocks/:id",async(e,t)=>{let n=await qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Sc={dotfiles:"allow"};v.get("/api/mocks/:id/png",async(e,t)=>{let n=await qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=zt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=_i(n,r,"png");if(!o)return t.status(404).json({error:"no image for this version (poster extraction failed)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Sc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});v.get("/api/mocks/:id/media",async(e,t)=>{let n=await qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=zt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=_i(n,r,"video");if(!o)return t.status(404).json({error:"this version has no video (image or html sourced)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Sc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});v.get("/api/mocks/:id/html",async(e,t)=>{let n=await qn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=zt(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(Nt(n,r.v).html,Sc,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});v.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&pu(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let d=a?await oe(a):null;if(!d&&c&&(d=(await ie()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+ke.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=(d.boxes||[]).find(m=>m.name===d.leadBox),p=await gS({trackId:d.id,name:i,html:n?null:s.html,image:r,video:o,cwd:d.cwd,width:l,group:u,box:d.leadBox,agent:f?.agent||null});t.json({ok:!0,mock:p})}catch(d){let f=d?.message||String(d);t.status(/required|invalid|must be/i.test(f)?400:500).json({error:f})}});v.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&pu(n)?n:null;try{let o=await yS(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});v.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await xS(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Bt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await LS(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await vS(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/flows",async(e,t)=>{try{t.json({flows:await NS({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/flows/:id",async(e,t)=>{let n=await PS(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});v.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ke.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await DS({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});v.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await BS(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/flows/:id",async(e,t)=>{try{t.json(await jS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ie(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+ke.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await VS({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/runbooks/:id",async(e,t)=>{let n=await Um(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});v.get("/api/runbooks/:id/md",async(e,t)=>{let n=await Um(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=KS(n,e.query.v??null);if(!r)return t.status(404).json({error:"no such version"});if(t.set("content-security-policy","sandbox"),t.set("cache-control","private, max-age=31536000, immutable"),t.type("text/markdown; charset=utf-8"),n.remote){let o=await zS(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(zn(n,r.v),Sc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});v.get("/api/loops",async(e,t)=>{t.json(await yi())});v.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ie()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+ke.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await yi();if(i.source!=="plane")return t.status(503).json({error:i.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${i.error||"no detail"})`});let c=i.loops.find(u=>u.trackId===a.id);if(!c)return t.status(404).json({error:`Track "${a.name}" is not a loop's run Track \u2014 nothing to report on`});let l=await nm({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});v.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ke.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await KE(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var lI=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await oe(n):null;if(!s&&r&&(s=(await ie()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ke.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await yi();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=zE(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};v.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await lI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await JE(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});v.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await lI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await YE(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var uI=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;v.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await oe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await ZE(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(uI(a)).json({error:a})}});v.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await oe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await QE(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(uI(s)).json({error:s})}});v.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await oe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await GE(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});v.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await oe(n):null;if(!a&&s&&(a=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ke.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await JS({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});v.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await YS(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});v.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await XS(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await ZS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/changelog/draft",async(e,t)=>{try{let n=o_(e.query.since||null);t.json({since:n,repos:Hw(),...await s_(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var dI=async({trackId:e,cwd:t})=>{let n=e?await oe(e):null;return!n&&t&&(n=(await ie()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+ke.sep)))||null),n};v.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await dI({trackId:o,cwd:s});if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await a_(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});v.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await dI({trackId:r,cwd:o});if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await i_(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var gH=120,og=400,ua=null,Y_=null;v.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>og)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${og} cap; a notification is a nudge, put the detail in chat`});let c=Ri(`${n||""}
|
|
663
|
-
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await
|
|
701
|
+
`));let p=l.map(k=>k.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await Pt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Ot({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Ix(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await cn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Ps(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Se(n).catch(()=>null),a=await jI({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Dt(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await Pt(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),u=s;u&&(await Ot({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await $x(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await cn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=Ar.filter(f=>e.body.actions.includes(f)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await F(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await lg(a,c,{actions:o,notify:s});if(f)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await Pt(a.id,Os(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await $({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&Dh(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});v.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await ug(o,{offerId:n,choice:r});t.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});var Rc=e=>t=>({...t,url:h_(t,e.headers)});v.get("/api/views",async(e,t)=>{try{let n=await ku({trackId:e.query.track||void 0});t.json({views:n.map(Rc(e)),maxLive:Br})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/views/:id",async(e,t)=>{let n=await $S(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Rc(e)(n),session:Hs(n.slug)})});v.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await te(n):null;if(!i&&s&&(i=(await le()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Hm({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});t.json({ok:!0,view:Rc(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});v.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Rc(e)(await RS(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Rc(e)(await OS(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/views/:id",async(e,t)=>{try{t.json(await CS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var iW=[["linear",Fo,Sw],["jira",nd,xw]],yg=new Map;v.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await kw(),r={};for(let[o,s,a]of iW){let i=await s();if(!i){yg.delete(o),r[o]={configured:!1};continue}let c=yg.get(o);if(!c||Date.now()-c.at>300*1e3){let l;try{l={configured:!0,ok:!0,...await a(i)}}catch(u){l={configured:!0,ok:!1,error:u?.message||String(u)}}c={at:Date.now(),body:l},yg.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await kw()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await VE(o),i=n?await te(n):null;!i&&r&&(i=(await le()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+xe.sep)))||null),i&&await $({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var Vo=async(e,t)=>{let n=e?await te(e):null;return!n&&t&&(n=(await le()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+xe.sep)))||null),n};v.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await hw()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});v.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Vo(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await oa(o);await Zu(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});v.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Vo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Zu(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var LI=async(e,t,n)=>{if(e)return{ref:e,track:await Vo(t,n)};let r=await Vo(t,n),o=r?await gw(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};v.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await LI(o,n,r),l=await LE({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});v.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await LI(e.query.circuit,e.query.track,e.query.cwd);t.json(await ww(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});v.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Vo(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=await HE(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...f})}catch(u){let f=u?.message||String(u);t.status(/required/i.test(f)?400:502).json({error:f})}});v.get("/api/decisions",async(e,t)=>{try{let n=await Vo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await WE(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});v.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await Vo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await gw(a.id);if(!i)return t.status(400).json({error:"this Track isn't in a circuit \u2014 link one first (bostrat-circuit link \u2026)"});let c=await FE(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});v.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await le()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await ia(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await te(r):null;if(!o&&n&&(o=(await le()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+xe.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await lA(),ticket:await In(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});v.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await In(e.params.id)})});v.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ia(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});v.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Pw(e.params.id)})});var Rd=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;v.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await pA(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Rd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Dw(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Rd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await mA(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Rd(r)).json({error:r})}});v.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await hA(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Rd(s)).json({error:s})}});v.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Zh(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});v.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await le()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await gA(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});v.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await wA()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});v.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await te(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await yA(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});v.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Bi({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/mocks/:id",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Oc={dotfiles:"allow"};v.get("/api/mocks/:id/png",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=tn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Pi(n,r,"png");if(!o)return t.status(404).json({error:"no image for this version (poster extraction failed)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Oc,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});v.get("/api/mocks/:id/media",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=tn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Pi(n,r,"video");if(!o)return t.status(404).json({error:"this version has no video (image or html sourced)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Oc,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});v.get("/api/mocks/:id/html",async(e,t)=>{let n=await Yn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=tn(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(Wt(n,r.v).html,Oc,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});v.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Tu(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await te(a):null;if(!f&&c&&(f=(await le()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+xe.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(h=>h.name===f.leadBox),p=await YS({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});t.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});v.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Tu(n)?n:null;try{let o=await JS(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});v.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await QS(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Pt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});v.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await gT(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await eT(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/flows",async(e,t)=>{try{t.json({flows:await fT({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/flows/:id",async(e,t)=>{let n=await pT(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});v.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await te(n):null;if(!a&&s&&(a=(await le()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await mT({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});v.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await hT(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/flows/:id",async(e,t)=>{try{t.json(await wT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await le(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+xe.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await ET({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.get("/api/runbooks/:id",async(e,t)=>{let n=await nh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});v.get("/api/runbooks/:id/md",async(e,t)=>{let n=await nh(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=ST(n,e.query.v??null);if(!r)return t.status(404).json({error:"no such version"});if(t.set("content-security-policy","sandbox"),t.set("cache-control","private, max-age=31536000, immutable"),t.type("text/markdown; charset=utf-8"),n.remote){let o=await AT(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Qn(n,r.v),Oc,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});v.get("/api/loops",async(e,t)=>{t.json(await Ti())});v.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await le()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+xe.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Ti();if(i.source!=="plane")return t.status(503).json({error:i.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${i.error||"no detail"})`});let c=i.loops.find(u=>u.trackId===a.id);if(!c)return t.status(404).json({error:`Track "${a.name}" is not a loop's run Track \u2014 nothing to report on`});let l=await fm({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});v.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await te(n):null;if(!s&&r&&(s=(await le()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await v_(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var Tg=async(e,t)=>{let n=e?await te(e):null;return!n&&t&&(n=(await le()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+xe.sep)))||null),n};v.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Tg(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,result:await Uv(s,o,{ship:{changedRepos:_o}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});v.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Tg(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await Wv(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});v.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await Tg(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:gm,...await zv(r)})});v.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s}=e.body||{};try{let a=await te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Kv(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Pt,unarchiveTrack:Fu})})}catch(a){let i=a?.message||String(a),c=a?.code==="removed"?410:/unknown offer/i.test(i)?404:/already answered/i.test(i)?409:/required|not on this card/i.test(i)?400:/unreachable/i.test(i)?503:502;t.status(c).json({error:i})}});v.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Hv(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});v.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await te(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await qv(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});ct.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||te(e.trackId).then(async s=>{if(s&&(n&&await Ai(s,n,o),t==="opened"||t==="pushed")){let a=Vv(await F(s).catch(()=>[]));a&&await Ai(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var FI=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await te(n):null;if(!s&&r&&(s=(await le()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+xe.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Ti();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=E_(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};v.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await FI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await A_(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});v.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await FI(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await __(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var UI=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;v.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await te(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await $_(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(UI(a)).json({error:a})}});v.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await te(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await R_(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(UI(s)).json({error:s})}});v.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await te(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await S_(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});v.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await te(n):null;if(!a&&s&&(a=(await le()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+xe.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await _T({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});v.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await IT(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});v.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await $T(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});v.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await RT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/changelog/draft",async(e,t)=>{try{let n=P_(e.query.since||null);t.json({since:n,repos:rg(),...await D_(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var HI=async({trackId:e,cwd:t})=>{let n=e?await te(e):null;return!n&&t&&(n=(await le()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+xe.sep)))||null),n};v.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await HI({trackId:o,cwd:s});if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await B_(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});v.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await HI({trackId:r,cwd:o});if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await j_(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var cW=120,bg=400,ga=null,_I=null;v.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>bg)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${bg} cap; a notification is a nudge, put the detail in chat`});let c=ji(`${n||""}
|
|
702
|
+
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await te(a):null;if(!l&&s&&(l=(await le()).find(h=>h.cwd&&(s===h.cwd||s.startsWith(h.cwd+xe.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,h)=>String(p).replace(/\s+/g," ").trim().slice(0,h),d={title:n?f(n,cW):l.name,body:f(i,bg),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await $(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),t.json({ok:!0,notification:d,delivered:ga?ga.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});v.post("/api/dictation",async(e,t)=>{if(!Buffer.isBuffer(e.body)||e.body.length===0)return t.status(400).json({error:"expected a raw audio body"});try{t.json(await xI(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});v.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await uc(),terms:await wg(),problem:await kI()})});v.post("/api/dictation/learn",async(e,t)=>{let{transcript:n,finalText:r}=e.body||{};if(typeof n!="string"||typeof r!="string")return t.status(400).json({error:"transcript and finalText are required"});t.json({learned:await bI(n,r)})});v.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await te(e.params.id))return t.status(404).json({error:"track not found"});t.json(await vI())});v.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});if(n.archivedAt)return t.status(409).json({error:"track is archived"});t.json(await SI())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});v.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=e.body||{};t.json({ok:!0,...await TI(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});v.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c,circuit:l}=e.body||{};try{let u=await ju({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),f=null;if(c)try{await ia(u,c,{source:"create"})}catch(p){f=p?.message||String(p)}let d=null;if(l)try{await Zu(u,await oa(l),{source:"picker"})}catch(p){d=p?.message||String(p)}t.json({ok:!0,track:u,...f?{ticketWarning:f}:{},...d?{circuitWarning:d}:{}})}catch(u){let f=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(f)?400:500).json({error:f})}});v.get("/api/tracks/:id",async(e,t)=>{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:WI(ra(e.params.id)),subtasks:jo(e.params.id).map(Cc),pendingOffers:await Oh(n)})});v.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Gh(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Pw(e.params.id).catch(()=>{}),t.json(n)}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});v.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await Do(e.params.id)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});v.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await Yh(e.params.id,e.body?.name)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/loop run Tracks/i.test(r)?409:/required|too long/i.test(r)?400:500;t.status(o).json({error:r})}});v.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await Fu(e.params.id,{freshSession:e.body?.freshSession===!0})})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});v.get("/api/tracks/:id/messages",async(e,t)=>{let n=await Nu(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});v.get("/api/tracks/:id/events",async(e,t)=>{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),t.flushHeaders();let r=i=>t.write(`id: ${i.seq}
|
|
664
703
|
data: ${JSON.stringify(i)}
|
|
665
704
|
|
|
666
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await F(n))i.seq>o&&r(i);let s=
|
|
705
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await F(n))i.seq>o&&r(i);let s=ny(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
667
706
|
|
|
668
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});v.get("/api/tracks/:id/search",async(e,t)=>{let n=await
|
|
669
|
-
`);v.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await
|
|
707
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});v.get("/api/tracks/:id/search",async(e,t)=>{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=oi(await F(n)),a=xl(s,r,{k:o,role:e.query.role||null,tag:e.query.tag||null});t.json({q:r,indexed:s.N,hits:a.map(i=>({score:Number(i.score.toFixed(3)),seq:i.chunk.seq,ts:i.chunk.ts,role:i.chunk.role,box:i.chunk.boxId,provider:i.chunk.provider,tags:i.chunk.tags,text:i.chunk.text}))})});v.get("/api/memory",async(e,t)=>{t.json({stores:await by(),harvest:hy()})});v.delete("/api/memory/:key/:file",async(e,t)=>{let n=String(e.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return t.status(400).json({error:"bad store key"});try{t.json({ok:!0,...await ky(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 lW=["Run a memory consolidation pass on this workspace's `.bostrat/memory/` directory:","1. Read `MEMORY.md` and every fact file.","2. Merge duplicates and facts that describe the same thing (keep the best-written one; fold unique details in; give the survivor a `supersedes:` entry naming what it absorbed, delete the absorbed file, and remove its index line).","3. Resolve contradictions in favor of the newest evidence \u2014 check the code where feasible before deciding.","4. Mark clearly-stale facts: correct them in place or delete them (file + index line).","5. Bump `metadata.modified` (ISO-8601, now) on every fact you touch.","6. Keep `MEMORY.md` one line per fact, under 200 lines.","When done, summarize what you merged, corrected, and deleted \u2014 that summary is the review record."].join(`
|
|
708
|
+
`);v.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Pt(e.params.id,lW)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});v.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await Uu(e.params.id,{freshSession:e.body?.freshSession===!0})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:n?.transferredTo||/mid-turn|terminal|archived/i.test(r)?409:500;t.status(o).json({error:r,...n?.transferredTo?{transferredTo:n.transferredTo}:{}})}});v.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r}=e.body||{};try{t.json(await Pt(e.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return t.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});if(o?.transferredTo)return t.status(409).json({error:s,transferredTo:o.transferredTo});t.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});v.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(n.archivedAt)return t.status(409).json({error:"track is archived"});if(!n.cwd)return t.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(e.body)?e.body:null,o=await DT(n.cwd,{filename:e.query.filename,buffer:r});t.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);t.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});v.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(!n.cwd)return t.status(404).json({error:"track has no workspace"});let r=xe.basename(String(e.params.file||""));if(!r||!sh(xe.extname(r).slice(1)))return t.status(400).json({error:"not an upload path"});t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox");let o=oh(n.cwd),s,a;try{s=await mt.promises.realpath(o),a=await mt.promises.realpath(xe.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+xe.sep))return t.status(404).json({error:"upload not found"});t.sendFile(r,{root:o},i=>{i&&!t.headersSent&&t.status(404).json({error:"upload not found"})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});var uW=/^[0-9a-fA-F-]{8,64}$/;v.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await te(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});let{box:r,uuid:o}=e.params,s=Number(e.params.idx);if(!uW.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=Jt(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=XH.createInterface({input:mt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let p;try{p=JSON.parse(d)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let f=l?_f(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",f.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(f.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await Bu(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});v.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await Nh(e.params.id);t.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){t.status(500).json({error:n?.message||String(n)})}});v.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o}=e.body||{};try{t.json(await Pu(e.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected/i.test(a)?409:500;t.status(i).json({error:a})}});v.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await qh({provider:e.params.provider,fresh:e.body?.fresh===!0});t.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);t.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});v.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await Mh(e.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;t.status(a).json({error:s})}});v.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Lh(e.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;t.status(a).json({error:s})}});v.get("/api/tracks/:id/usage",async(e,t)=>{let n=await te(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=e.query.fresh==="1"||e.query.fresh==="true";t.json(await hd(r,{fresh:o}))});v.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Fh(e.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;t.status(s).json({error:o})}});v.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await cw(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Cc(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return t.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;t.status(c).json({error:i})}});v.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:jo(e.params.id).map(Cc)})});v.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Ch(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});v.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:Cc(Ku(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});v.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:Cc(lw(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Cc(e){return e?{id:e.id,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,step:e.step,running:!e.finishedAt,box:e.box,branch:e.branch,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}v.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await Hu(e.params.id,n,r!==!1,o);t.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);if(s?.usageCap)return t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});t.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});v.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await Bh(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});v.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(WI(ra(e.params.id))??{running:!1})});v.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await nw(e.params.id);t.json({ok:!0,step:n.step,startedAt:n.startedAt})}catch(n){let r=n?.message||String(n);t.status(/already running/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});v.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=tw(e.params.id);if(!n)return t.json({running:!1});t.json({running:!n.finishedAt,step:n.step,error:n.error,startedAt:n.startedAt,finishedAt:n.finishedAt,...n.result??{}})});v.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await Wu(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:400).json({error:r})}});v.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await sw({...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 WI(e){return e?{running:!e.finishedAt,step:e.step,error:e.error,fromBox:e.fromBox,toBox:e.toBox,toAgent:e.toAgent,skipPermissions:e.skipPermissions,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}v.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:el(),paths:Py,trace:await Ny({limit:n})})});var kg=xe.resolve(OI,"..","..","client","dist");mt.existsSync(kg)&&(v.use(Gt.static(kg)),v.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(xe.join(kg,"index.html"),Oc)));var qI=QH.createServer(v),dW=new tW({noServer:!0}),KI=(e,t,n)=>{if(!(vc(e)||wa.allowed(wa.clientIp(e))||vg.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
670
709
|
\r
|
|
671
|
-
`),t.destroy();return}if(
|
|
710
|
+
`),t.destroy();return}if(m_(e,t,n))return;if(!f_(e,MI,{enforce:NI})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
672
711
|
\r
|
|
673
|
-
`),t.destroy();return}let r=
|
|
712
|
+
`),t.destroy();return}let r=eb(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
674
713
|
\r
|
|
675
|
-
`),t.destroy();return}let o=r.sessionName;if(!
|
|
714
|
+
`),t.destroy();return}let o=r.sessionName;if(!Pe(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
676
715
|
\r
|
|
677
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
716
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;dW.handleUpgrade(e,t,n,i=>Xy(i,o,{cols:s,rows:a}))};qI.on("upgrade",KI);var GI=process.env.BOSTRAT_CERT_DIR||xe.join(process.env.HOME||_d.tmpdir(),".bostrat","certs"),II=Number(process.env.BOSTRAT_TLS_PORT)||3443,$I=xe.join(GI,"bostrat.globopi.key"),RI=xe.join(GI,"bostrat.globopi.crt");if(mt.existsSync($I)&&mt.existsSync(RI)){let e=eW.createServer({key:mt.readFileSync($I),cert:mt.readFileSync(RI)},v);e.on("upgrade",KI),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(II,Id,()=>{console.log(`[bostrat-agent] https listening on ${Id||"0.0.0.0"}:${II}`)})}qI.listen(EI,Id,()=>{console.log(`[bostrat-agent] listening on ${Id||"0.0.0.0"}:${EI}`);let e=SE(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: ${$t}`),console.log(`[bostrat-agent] auto-trigger default: ${CI}%`),console.log(`[bostrat-agent] ip allowlist: ${wa.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(vg.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),ew().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),aw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Jh().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(()=>Xh()).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)),Wh().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)),bW(),mW(),hW(),yW(),Y0({listTracks:le,getTrack:te}).then(t=>console.log(`[bostrat-agent] spend caps ${t.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(t.spent).length} provider counter(s)`)).catch(t=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",t?.message||t)),UT()&&HT({listTracks:le}),By().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)),K_(),J_(),bA({resolveTrack:te}),$A({listTracks:le,createTrack:ju,archiveTrack:Do,listRepos:On,resolveTrack:te,sayToLead:Pt,answerPrompt:Pu,startLeadHandoff:Hu,interruptLead:Bu,listSubtaskJobs:jo,cancelSubtask:Ku,readEvents:F,appendEvent:$,divertShip:lg,answerPrExists:ug,knownAgents:Xi}),fW(),Ha().then(t=>{t||console.warn(`[boot] no host tmux server on ${$t} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),uI(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(ga=ME({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||_d.hostname(),name:process.env.BOSTRAT_NODE_NAME||_d.hostname(),listTracks:le,detectAgents:Mu,sampleMetrics:zA,onTracksTransferred:ow,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||_d.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(()=>{}),_I=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(()=>{}),_I=null},onUpdateAvailable:async t=>{try{xg={latest:t.latest,current:t.current},await An({...t,policy:kt()}),kt()==="auto"&&await PI({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>sW(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await An({current:t,latest:a,min:n,installKind:o,required:!0,policy:kt(),status:"available",reason:"refused"}),kt()!=="off"&&a){let i=await Xu({to:a,kind:Kt,reason:"hard-floor"});i.ok?($c.cancel(),await An({current:t,latest:a,min:n,installKind:o,required:!0,policy:kt(),status:"updating",reason:"hard-floor"})):console.error(`[self-update] hard-floor update not started: ${i.reason} \u2014 ${s}`)}}catch(a){console.error(`[plane-link] hard-floor handling failed: ${a?.message||a}`)}},updatePolicy:kt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),os().catch(()=>{}),MT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),kA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),rT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),kT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),Bx().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{ga?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function fW(){pW(),bu().catch(()=>{}),setInterval(()=>bu().catch(()=>{}),1e4)}function pW(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-followup","bostrat-ship","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=xe.resolve(OI,"..","bin",e),n=xe.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!mt.existsSync(t))continue;mt.mkdirSync(n,{recursive:!0});let r=xe.join(n,e);(mt.existsSync(r)?mt.readlinkSync(r):null)!==t&&(mt.rmSync(r,{force:!0}),mt.symlinkSync(t,r)),console.log(`[bostrat-agent] ${e} CLI linked at ${r}`)}catch(t){console.error(`[bostrat-agent] ${e} CLI link failed:`,t?.message||t)}}function mW(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await le(),r=await XA({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 hW(){let e=null,t=async()=>{try{let n=bs(),r=0,o=[],s=null;try{let l=await le(),u=await Vw(l);r=u.fleet.unpricedTurns||0,o=u.fleet.unpricedModels||[],s=await YA(l,{days:7})}catch{}let a=[...n.problems];s&&!s.ok&&a.push(`${s.divergent} recent row(s) diverge from the table (implied-rate audit) \u2014 run \`npm run cost -- --audit\``);let i=o.filter(l=>l!=="unknown");i.length&&a.push(`${r} unpriced turn(s) \u2014 add rates for: ${i.join(", ")}`);let c=a.join(`
|
|
678
717
|
`);if(!a.length){e!==""&&console.log(`[pricing] table ok \u2014 rates verified ${n.tableVerifiedAt??"never"}`),e="";return}for(let l of a)console.warn(`[pricing] \u26A0 ${l}`);c!==e&&process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: cost instrument needs attention",Tags:"warning"},body:a.join(`
|
|
679
|
-
`).slice(0,900)}).catch(()=>{}),e=c}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var
|
|
718
|
+
`).slice(0,900)}).catch(()=>{}),e=c}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var wW=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,gW=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function yW(){let e=async()=>{try{let n=await yy();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await le()).filter(a=>!Ve(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await gy([...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,wW).unref()},gW).unref()}function bW(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await le();for(let n of t)Ve(n)||!n.boxes?.some(r=>r.state==="live")||(await Nu(n.id).catch(()=>{}),await PS(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|