bostrat-node 1.17.3 → 1.17.5
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-audit +9 -9
- package/bin/bostrat-blueprint +1 -1
- package/bin/bostrat-replay +1 -1
- package/bin/bostrat-ship +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.js +324 -319
- package/package.json +1 -1
package/bin/bostrat-audit
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import ye from"node:fs/promises";import Ho from"node:os";import K from"node:path";var Vt=(e,t)=>{if(e==null||e==="")return t;let n=Number(e);return Number.isFinite(n)&&n>=0?n:t},Wo=Vt(process.env.BOSTRAT_HOOK_FOLD_MS,250);var zt=1e3;function q(e){let t=Number(e?.count);return Number.isInteger(t)&&t>1&&e?.tools&&typeof e.tools=="object"?Math.min(t,zt):1}function P(e){let t=e||{},n=q(t);if(n===1)return[t];let o=(Array.isArray(t.calls)?t.calls:[]).filter(r=>r&&typeof r=="object").slice(0,n),s={};for(let r of o)r.tool&&(s[r.tool]=(s[r.tool]||0)+1);for(let[r,a]of Object.entries(t.tools))for(let u=s[r]||0;u<Number(a)&&o.length<n;u++)o.push({tool:r});for(;o.length<n;)o.push({tool:null});return o}import{createHash as Yt}from"node:crypto";var Jt=new Set(["userId"]);function ke(e){return e===null||typeof e!="object"?e===void 0||typeof e=="number"&&!Number.isFinite(e)?"null":JSON.stringify(e):Array.isArray(e)?`[${e.map(n=>ke(n===void 0?null:n)).join(",")}]`:`{${Object.keys(e).filter(n=>e[n]!==void 0).sort().map(n=>`${JSON.stringify(n)}:${ke(e[n])}`).join(",")}}`}function oe(e){let t={};for(let n of Object.keys(e))Jt.has(n)||(t[n]=e[n]);return Yt("sha256").update(ke(t)).digest("hex")}var Ee=e=>Number.isFinite(e?.seq);function Ye(e){let t={members:0,chained:0,preChain:0,intactFrom:null,intactTo:null,genesis:!1,breaks:[]},n=null,o=!1,s=(r,a)=>{n={seq:r.seq,hash:a},t.intactFrom=r.seq,t.genesis=r.seq===1&&r.prev===null};for(let r of e||[]){if(!Ee(r))continue;if(t.members++,r.prev===void 0){n&&!o?(t.breaks.push({kind:"unchained",seq:r.seq,eventId:r.id??null}),o=!0):n||t.preChain++;continue}t.chained++;let a=oe(r);if(!n||o){o=!1,s(r,a);continue}if(r.seq===n.seq+1){if(r.prev===n.hash){n={seq:r.seq,hash:a};continue}t.breaks.push({kind:"prev-mismatch",seq:r.seq,eventId:r.id??null,expected:n.hash,got:r.prev})}else r.seq>n.seq+1?t.breaks.push({kind:"seq-gap",seq:r.seq,eventId:r.id??null,from:n.seq+1,to:r.seq-1}):t.breaks.push({kind:"seq-repeat",seq:r.seq,eventId:r.id??null,headSeq:n.seq});s(r,a)}return t.intactTo=n?n.seq:null,n||(t.intactFrom=null),t}var B=6e4,Zt=120*B,Je=2e3,Ze=5e3,Qt=2*B,en=5e3,tn=2*B,Z=5e3,nn={Workflow:360*B,Agent:60*B},on=5*B,sn=10*B,xe=80,M=3,rn=["Checkpoint: write a work-in-progress handoff","Resume from .bostrat/handoff/latest.md","Beat: write a","You are a SUBTASK agent working in an isolated","A subtask box you delegated has finished"],Ae={operator:"strong",orchestrator:"strong","card-answer":"strong",loop:"strong","background-job":"strong","task-continuation":"strong","operator-terminal":"weak","orchestrator-unlogged":"weak","background-launch":"weak","task-continuation-unlogged":"weak",peer:"flagged","nested-session":"flagged","unknown-prompt":"flagged",unrooted:"flagged"},tt={claude:{source:"hook",note:"hook-sourced: the CLI's hooks record the turn start (prompt_id), each call's intent and outcome (tool_use_id), failed calls and the Stop; the transcript is the duplicate"},codex:{source:"transcript",note:"transcript-sourced (a file the agent writes): the rollout names each turn (turn_id, turn_trigger, parent_turn_id) and each call (call_id), so a call cites the send that opened its turn; no intent before a call, no failed-call record, no turn end on the log, and a prompt typed at the pane is not on the log"},cursor:{source:"transcript",note:"transcript-sourced (a store the agent writes): message order is the turn structure, so a call cites the user message that opened its turn (its content hash); no turn ids, no per-message time (ts is synthesized), no intent, no failed-call record, no turn end, and a prompt typed at the pane is not on the log"}},C=e=>String(e??"").replace(/\s+/g," ").trim(),y=e=>e?.ts??0,Ie=(e,t)=>!!e&&!!t&&(e.startsWith(t.slice(0,xe))||t.startsWith(e.slice(0,xe))),an=6,cn=(e,t)=>Ie(e,t)||e.length>=an&&t.includes(e.slice(0,xe)),ln=/^<pasted_content\b[^>]*>\s*/i,Te=e=>C(e.payload?.promptHead??e.payload?.text).replace(ln,""),un=/^<cross-session-message\b/i,dn=/^<task-notification>/i,pn=/<tool-use-id>\s*([^<\s]+)\s*<\/tool-use-id>/i;function $e(e){let t=e.payload||{},n=q(t),o=P(t)[0],s=o.head||o.path||(t.command?C(t.command):""),r=e.type==="tool.failed"?" [failed]":e.type==="tool.started"?" [never completed]":"";return`${o.tool||"tool"}${s?`: ${s.slice(0,100)}`:""}${r}${n>1?` (+${n-1} more)`:""}`}var fn=/\.answered$/,hn=new Set(["loop.created","followup.created"]),mn=16,nt=new Set(["tool.started","tool.called","tool.failed"]);function gn(){return{byId:new Map,byDelivery:new Map,byPrompt:new Map,byToolUse:new Map,jobs:new Map}}function yn(e,t){if(!t||typeof t.type!="string")return;t.id&&e.byId.set(t.id,t);let n=t.payload||{};if(t.type==="turn.user")t.source==="orchestrator"&&n.deliveryId&&e.byDelivery.set(n.deliveryId,t),t.source==="hook"&&n.promptId&&!e.byPrompt.has(`${t.boxId}\0${n.promptId}`)&&e.byPrompt.set(`${t.boxId}\0${n.promptId}`,t);else if(t.type==="job.started"&&n.id)e.jobs.set(n.id,t);else if(nt.has(t.type)&&t.source==="hook")for(let o of P(n))o.toolUseId&&!e.byToolUse.has(o.toolUseId)&&e.byToolUse.set(o.toolUseId,t)}function bn(e){let t=gn();for(let n of e)yn(t,n);return t}function Qe(e,t){let n=e;for(let o=0;n&&o<mn;o++){let s=n.payload||{},r=n.type;if(n.causationId&&t.byId.has(n.causationId)&&n.causationId!==n.id){n=t.byId.get(n.causationId);continue}if(nt.has(r)){let a=s.promptId?t.byPrompt.get(`${n.boxId}\0${s.promptId}`):null;if(!a)return null;n=a;continue}if(r==="turn.user"){if(n.source==="hook"){if(s.kind==="task-notification"&&s.jobId){let u=t.jobs.get(s.jobId);if(!u)return null;n=u;continue}let a=s.deliveryId?t.byDelivery.get(s.deliveryId):null;if(!a)return null;n=a;continue}return n.source==="orchestrator"?s.loopId||s.producer==="loop"?{origin:"loop",root:n}:{origin:n.actor==="orchestrator"?"orchestrator":"operator",root:n}:n.source==="transcript"?{origin:"operator-terminal",root:n}:null}if(r==="job.started"){let a=s.id?t.byToolUse.get(s.id):null;if(!a)return null;n=a;continue}return fn.test(r)||hn.has(r)?{origin:"card-answer",root:n}:r==="peer.message"?{origin:"peer",root:n}:null}return null}function wn(e){let t={events:e.length,firstSeq:null,lastSeq:null,headMissing:!1,gaps:0,repeats:0},n=null;for(let s of e)Number.isFinite(s?.seq)&&(n===null?t.firstSeq=s.seq:s.seq>n+1?t.gaps++:s.seq<=n&&t.repeats++,n=s.seq);t.lastSeq=n,t.headMissing=t.firstSeq!==null&&t.firstSeq>1;let o=Ye(e);return t.chain={state:o.chained===0?"pre-chain":o.breaks.length?"broken":o.genesis?"intact":"intact-from-mid-log",chained:o.chained,preChain:o.preChain,intactFrom:o.intactFrom,intactTo:o.intactTo,breaks:o.breaks},t}var Sn=e=>{switch(e.kind){case"prev-mismatch":return`seq ${e.seq} states a predecessor that is not the line before it \u2014 the line before it changed, or was replaced`;case"seq-gap":return`seq ${e.from}${e.to!==e.from?`\u2013${e.to}`:""} missing between chained lines`;case"seq-repeat":return`seq ${e.seq} appears again after the chain passed it (a line was inserted, or the log was rewound)`;case"unchained":return`seq ${e.seq} carries no chain link after chained lines (an older node resumed this log)`;default:return`${e.kind} at seq ${e.seq}`}};function kn(e){let t=new Map,n=new Map,o=new Map,s=new Map,r=(i,d,m)=>{i.has(d)||i.set(d,[]),i.get(d).push(m)};for(let i of e)if((i.type==="box.state.changed"||i.type.startsWith("handoff."))&&r(s,i.payload?.box||i.boxId||null,y(i)),i.type==="session.ready"&&([...s.get(i.boxId)||[],...s.get(null)||[]].some(m=>m<=y(i)&&y(i)-m<=tn)||r(o,i.boxId,y(i))),i.type==="turn.user")if(i.source==="orchestrator"){let d=(i.payload?.attachments||[]).map(m=>m?.id).filter(Boolean);r(t,i.boxId,{ts:y(i),head:C(i.payload?.text),files:d,actor:i.actor,usedAt:null})}else i.source==="transcript"&&r(n,i.boxId,{ts:y(i),head:C(i.payload?.text),note:i.payload?.note?.kind||null});let a=new Map,u=new Map,l=new Map;for(let i of e){if(i.type==="turn.user"&&i.source==="orchestrator"&&u.set(i.boxId,!0),i.source!=="hook")continue;if(i.type==="turn.agent"){let h=l.get(i.boxId)||0;h>0?l.set(i.boxId,h-1):u.set(i.boxId,!1);continue}if(i.type!=="turn.user")continue;let d=Te(i),m=(()=>{if(i.actor==="peer"||un.test(d))return{origin:"peer"};if(dn.test(d))return{origin:"task-continuation",jobId:d.match(pn)?.[1]||null};let h=(t.get(i.boxId)||[]).filter(b=>(b.usedAt===null||y(i)-b.usedAt<=Ze)&&b.ts>=y(i)-Zt&&b.ts<=y(i)+Je&&(cn(b.head,d)||!b.head&&b.files.length>0&&(b.files.some(x=>d.includes(`.bostrat/uploads/${x}`))||y(i)-b.ts<=Ze))),g=h[h.length-1];if(g)return g.usedAt=y(i),{origin:g.actor==="orchestrator"?"orchestrator":"operator"};if(rn.some(b=>d.startsWith(b)))return{origin:"orchestrator-unlogged"};if((n.get(i.boxId)||[]).find(b=>!b.note&&b.ts>=y(i)-Je&&b.ts<=y(i)+Qt&&Ie(b.head,d)))return{origin:"operator-terminal"};let j=(o.get(i.boxId)||[]).some(b=>b<=y(i)&&y(i)-b<=en);return{origin:u.get(i.boxId)||j?"nested-session":"unknown-prompt"}})();a.set(i,m),m.origin==="nested-session"?l.set(i.boxId,(l.get(i.boxId)||0)+1):u.set(i.boxId,!0)}return a}function En(e){let t=new Map,n=new Map;for(let o of e){let s=o.payload?.id;if(s)if(o.type==="job.started"){let r={from:(o.payload?.ts??y(o))-Z,to:1/0,loggedAt:y(o)};n.set(s,r),t.has(o.boxId)||t.set(o.boxId,[]),t.get(o.boxId).push(r)}else o.type==="job.stopped"&&n.has(s)&&(n.get(s).to=y(o)+Z)}return{byBox:t,byId:n}}function $n(e,{trackId:t=null,name:n=null,since:o=0}={}){let s=kn(e),r=En(e),a=new Set;for(let c of e)c.type==="tool.called"&&c.source==="hook"&&a.add(c.boxId);let u=c=>c.type==="tool.called"&&(c.source==="hook"||!(c.provider==="claude"&&a.has(c.boxId)))||c.type==="tool.failed"&&c.source==="hook",l=new Set,i=new Map;for(let c of e)if(c.source==="hook")if(c.type==="tool.called"||c.type==="tool.failed")for(let p of P(c.payload))p.toolUseId&&l.add(p.toolUseId);else c.type==="audit.flagged"&&c.payload?.denied===!0&&c.payload.toolUseId?l.add(c.payload.toolUseId):(c.type==="turn.agent"||c.type==="session.ready"||c.type==="turn.aborted"||c.type==="turn.interrupted")&&(i.has(c.boxId)||i.set(c.boxId,[]),i.get(c.boxId).push(y(c)));let d=e.length?y(e[e.length-1]):0,m=c=>{if(c.type!=="tool.started"||c.source!=="hook")return null;let p=P(c.payload).filter($=>$.toolUseId&&!l.has($.toolUseId));return p.length&&((i.get(c.boxId)||[]).some($=>$>y(c))||d-y(c)>sn)?p:null},h={},g={},S={keyed:0,turn:0,timing:0},j=bn(e),b=new Map,x=(c,p,f,$,_,w=1)=>{let U=`${c}\0${f.boxId}\0${$}`,N=b.get(U);return(!N||y(f)-N.to>on)&&(N={rule:c,severity:p,trackId:t,track:n,boxId:f.boxId,from:y(f),to:y(f),count:0,note:_,samples:[]},b.set(U,N),T.push(N)),N.to=y(f),N.count+=w,N},T=[],v=new Map,k=c=>(v.has(c)||v.set(c,{open:!1,origin:null,nested:0,soft:null,lastSendHead:null,launch:null}),v.get(c));for(let c of e){let p=k(c.boxId),f=y(c)>=o;if(c.type==="session.foreign"){if(c.payload?.phase==="prompted"&&f){let E=x("nested-session","medium",c,"","a session that is not the box's own was prompted through this box \u2014 typically an agent-launched `claude -p`");E.samples.length<M&&E.samples.push(C(c.payload?.promptHead).slice(0,100))}continue}if(c.type==="turn.user"&&c.source==="hook"){let E=s.get(c);if(E.origin==="nested-session"){if(p.nested++,f){let W=x("nested-session","medium",c,"","a prompt no recorded send accounts for arrived mid-turn or right after a SessionStart \u2014 typically an agent-launched `claude -p`");W.samples.length<M&&W.samples.push(Te(c).slice(0,100))}continue}if(p.open=!0,p.origin=E.origin,E.origin==="task-continuation"&&!r.byId.has(E.jobId)&&(p.origin="task-continuation-unlogged"),p.origin==="unknown-prompt"&&f){let W=x("unknown-prompt","medium",c,"","a turn opened by a prompt with no operator, machinery or job record behind it");W.samples.length<M&&W.samples.push(Te(c).slice(0,100))}}else if(c.type==="turn.agent"&&c.source==="hook")p.nested>0?p.nested--:p.open=!1;else if(c.type==="turn.aborted"||c.type==="turn.interrupted")p.open=!1;else if(c.type==="turn.user"&&c.source==="orchestrator")p.lastSendHead=C(c.payload?.text),p.soft=c.actor==="orchestrator"?"orchestrator":"operator",p.open=!0,p.origin=p.soft;else if(c.type==="turn.user"&&c.source==="transcript"){let E=C(c.payload?.text);!c.payload?.note&&!Ie(E,p.lastSendHead)&&(p.soft="operator-terminal")}else c.type==="peer.message"&&(p.soft="peer");let $=m(c);if(!$&&!u(c))continue;let _=$?$.length:c.source==="hook"?q(c.payload):1,w;if(c.payload?.foreignSession)w="nested-session";else if(c.source==="hook"){let E=(r.byBox.get(c.boxId)||[]).some(H=>y(c)>=H.from&&y(c)<=H.to);p.open?w=p.origin:E?w="background-job":p.nested>0?w="nested-session":p.launch&&y(c)<=p.launch?w="background-launch":w="unrooted";let W=(r.byBox.get(c.boxId)||[]).some(H=>H.loggedAt<=y(c)+Z);for(let H of P(c.payload)){let Se=H.tool,Xt=!!H.toolUseId&&r.byId.has(H.toolUseId);(Se==="Workflow"&&!Xt||Se==="Agent"&&!W)&&p.open&&Ae[p.origin]!=="flagged"&&(p.launch=Math.max(p.launch||0,y(c)+nn[Se]))}}else c.payload?.turnId?w=String(c.payload.turnTrigger||"user")==="user"?"operator-terminal":"unknown-prompt":w=p.soft||"unrooted";let U=c.payload?.foreignSession?null:Qe(c,j);if(U&&(w=U.origin),!f)continue;let N=!U&&c.source!=="hook"&&!!c.payload?.turnId;S[U?"keyed":N?"turn":"timing"]+=_,h[w]=(h[w]||0)+_;let G=c.provider||"unknown";if(g[G]||={actions:0,fidelity:c.source==="hook"?"hook":"transcript",identified:!1,turnKeyed:!1,rooted:{key:0,turn:0,timing:0}},g[G].actions+=_,g[G].rooted[U?"key":N?"turn":"timing"]+=_,c.source==="hook"&&(g[G].fidelity="hook"),c.source==="hook"&&P(c.payload).some(E=>E.toolUseId)&&(g[G].identified=!0),c.source!=="hook"&&c.payload?.turnId&&(g[G].turnKeyed=!0),w==="unknown-prompt"&&c.source!=="hook"){let E=x("unexplained-turn","medium",c,String(c.payload.turnId),`calls in a transcript turn the CLI says no prompt opened (turn_trigger: ${String(c.payload.turnTrigger)}) and that cites no recorded cause`,_);E.samples.length<M&&E.samples.push($e(c));continue}if($){let E=x("unfinished-calls","low",c,"","calls the CLI began (PreToolUse) and never reported complete or failed \u2014 killed, crashed or restarted mid-call; the tool may have run",_);E.samples.length<M&&E.samples.push($e(c))}if(w==="unrooted"){let E=x("unrooted-actions","high",c,"","tool calls with no open turn, no recorded job and no launch that accounts for them",_);E.samples.length<M&&E.samples.push($e(c))}else if(w==="peer")x("peer-driven-actions","low",c,"","tool calls in a turn another session's message opened",_);else if(w==="nested-session"||w==="unknown-prompt"){let E=b.get(`${w}\0${c.boxId}\0`);E&&(E.actions=(E.actions||0)+_)}}let D={total:0,keyed:0,timing:0,unrooted:0};{let c=new Map,p=new Map;for(let f of e)f.source==="hook"&&(f.type==="turn.user"?p.set(f.boxId,y(f)):(f.type==="turn.agent"||f.type==="turn.aborted"||f.type==="turn.interrupted")&&p.has(f.boxId)&&((c.get(f.boxId)||c.set(f.boxId,[]).get(f.boxId)).push([p.get(f.boxId),y(f)]),p.delete(f.boxId)));for(let[f,$]of p)(c.get(f)||c.set(f,[]).get(f)).push([$,1/0]);for(let f of e){if(f.type!=="code.committed"||f.source!=="hook"||y(f)<o)continue;D.total++;let $=Number(f.payload?.ts??f.ts)||y(f);if(Qe(f,j))D.keyed++;else if((c.get(f.boxId)||[]).some(([_,w])=>$>=_-Z&&$<=w+Z))D.timing++;else{D.unrooted++;let _=x("unrooted-commit","medium",f,"","a commit landed in the box's workspace with no turn open and no recorded cause");_.samples.length<M&&_.samples.push(`${String(f.payload?.sha||"").slice(0,7)} ${C(f.payload?.subject).slice(0,80)}`)}}}{let c=e.filter(p=>p.type==="audit.flagged"&&p.source!=="plane"&&p.payload?.denied===!0&&y(p)>=o);if(c.length){let p=[...new Set(c.map(f=>f.payload?.rule).filter(Boolean))];T.push({rule:"guard-denied",severity:"high",trackId:t,track:n,boxId:c[0].boxId??null,from:y(c[0]),to:y(c.at(-1)),count:c.length,samples:c.slice(0,M).map(f=>`${f.payload?.rule||"?"}: ${C(f.payload?.samples?.[0]||f.payload?.tool||"")}`.slice(0,160)),note:`${c.length} call(s) denied before running by the node's PreToolUse guard (${p.join(", ")}) \u2014 BOSTRAT_AUDIT_GUARD is on for this node`})}}let O=wn(e),F=Object.values(h).reduce((c,p)=>c+p,0);if(O.headMissing&&F&&T.push({rule:"log-head-missing",severity:"medium",trackId:t,track:n,boxId:null,from:y(e[0]),to:y(e[0]),count:1,samples:[],note:`the log starts at seq ${O.firstSeq} \u2014 its earlier history is not on this node`}),O.chain.breaks.length){let c=new Map;for(let w of e)Number.isFinite(w?.seq)&&!c.has(w.seq)&&c.set(w.seq,w);let p=O.chain.breaks.filter(w=>w.kind==="prev-mismatch"||w.kind==="seq-repeat"),f=w=>y(c.get(w.seq))||y(e.at(-1)),$=O.chain.breaks[0],_=O.chain.breaks.at(-1);T.push({rule:"chain-broken",severity:p.length?"high":"medium",trackId:t,track:n,boxId:null,from:f($),to:f(_),count:O.chain.breaks.length,samples:O.chain.breaks.slice(0,M).map(Sn),note:`the log's hash chain breaks ${O.chain.breaks.length} time(s) on this node \u2014 advisory: the plane's verified head is the anchor (GET /plane/admin/chain/${t??"<track>"})`})}return{trackId:t,track:n,actions:F,byOrigin:h,byEvidence:S,byProvider:g,commits:D,findings:T,integrity:O}}var et={high:0,medium:1,low:2};function ot(e,{days:t=14,now:n=Date.now()}={}){let o=t?n-t*864e5:0,s={days:t||null,tracks:0,actions:0,byOrigin:{},byStrength:{strong:0,weak:0,flagged:0},byEvidence:{keyed:0,turn:0,timing:0},commits:{total:0,keyed:0,timing:0,unrooted:0},byProvider:{},findings:[],integrity:{logs:0,headMissing:0,withGaps:0,withRepeats:0,chain:{intact:0,intactFromMidLog:0,broken:0,preChain:0}}};for(let r of e||[]){let a=$n(r.events||[],{trackId:r.trackId,name:r.name,since:o});s.integrity.logs++,a.integrity.headMissing&&s.integrity.headMissing++,a.integrity.gaps&&s.integrity.withGaps++,a.integrity.repeats&&s.integrity.withRepeats++;let u=a.integrity.chain?.state;if(u==="intact"?s.integrity.chain.intact++:u==="intact-from-mid-log"?s.integrity.chain.intactFromMidLog++:u==="broken"?s.integrity.chain.broken++:s.integrity.chain.preChain++,!(!a.actions&&!a.findings.length)){s.tracks++,s.actions+=a.actions;for(let l of Object.keys(s.byEvidence))s.byEvidence[l]+=a.byEvidence?.[l]||0;for(let l of Object.keys(s.commits))s.commits[l]+=a.commits?.[l]||0;for(let[l,i]of Object.entries(a.byOrigin))s.byOrigin[l]=(s.byOrigin[l]||0)+i,s.byStrength[Ae[l]||"flagged"]+=i;for(let[l,i]of Object.entries(a.byProvider)){s.byProvider[l]||={actions:0,fidelity:i.fidelity,identified:!1,turnKeyed:!1,rooted:{key:0,turn:0,timing:0}},s.byProvider[l].actions+=i.actions,i.fidelity==="hook"&&(s.byProvider[l].fidelity="hook"),i.identified&&(s.byProvider[l].identified=!0),i.turnKeyed&&(s.byProvider[l].turnKeyed=!0);for(let d of["key","turn","timing"])s.byProvider[l].rooted[d]+=i.rooted?.[d]||0}s.findings.push(...a.findings)}}for(let[r,a]of Object.entries(s.byProvider))a.note=tt[r]?.note??(a.fidelity==="hook"?"hook-sourced":"transcript-sourced (a record the agent writes); no turn structure read by its adapter \u2014 calls attributed to the latest prompt");return s.findings.sort((r,a)=>et[r.severity]-et[a.severity]||a.to-r.to),s.ok=!s.findings.some(r=>r.severity==="high"),s}var J=(e,t)=>t?`${(e/t*100).toFixed(e&&e/t<.001?2:1)}%`:"0%",_e=e=>new Date(e).toISOString().slice(0,16).replace("T"," ");function st(e,{limit:t=20,containment:n=null}={}){let o=[];o.push(`AGENT ACTION AUDIT \xB7 ${e.days?`last ${e.days}d`:"all history"} \xB7 ${e.actions} action(s) across ${e.tracks} Track(s)`),o.push(""),o.push("Who asked for it");for(let l of["strong","weak","flagged"]){let i=Object.entries(e.byOrigin).filter(([d])=>(Ae[d]||"flagged")===l).sort((d,m)=>m[1]-d[1]);if(i.length){o.push(` ${l.toUpperCase()} \xB7 ${e.byStrength[l]} (${J(e.byStrength[l],e.actions)})`);for(let[d,m]of i)o.push(` ${d.padEnd(27)} ${String(m).padStart(7)} ${J(m,e.actions)}`)}}o.push("");let s=e.byEvidence||{keyed:0,turn:0,timing:0};o.push(`Rooted by recorded key \xB7 ${s.keyed} (${J(s.keyed,e.actions)})`+(s.turn?` \xB7 by its transcript turn only \xB7 ${s.turn} (${J(s.turn,e.actions)})`:"")+` \xB7 by timing \xB7 ${s.timing} (${J(s.timing,e.actions)})`);let r=e.commits||{total:0,keyed:0,timing:0,unrooted:0};r.total&&o.push(`Commits \xB7 ${r.total}: ${r.keyed} cite their turn, ${r.timing} inside an open turn, ${r.unrooted} unrooted`),o.push(""),o.push("Evidence by provider");for(let[l,i]of Object.entries(e.byProvider).sort((d,m)=>m[1].actions-d[1].actions)){let d=i.rooted||{key:0,turn:0,timing:0},m=i.fidelity==="hook"?`${d.key} by recorded key \xB7 ${d.timing} by timing`:i.turnKeyed?`${d.key} cite the send that opened their turn \xB7 ${d.turn} cite only their transcript turn \xB7 ${d.timing} by timing (no turn key)`:"all by timing (attributed to the latest prompt)";o.push(` ${l.padEnd(10)} ${String(i.actions).padStart(7)} `+(i.fidelity==="hook"?`hook-recorded (turn boundaries known${i.identified?"; calls identified, intent and outcome paired":""})`:i.turnKeyed?"transcript-recorded (agent-written; turns keyed by the CLI's own record)":"transcript only (agent-written; no turn boundaries)")+` \xB7 ${m}`);let h=i.note??tt[l]?.note;h&&o.push(` ${h}`)}o.push("");let a=e.integrity,u=a.chain||{intact:0,intactFromMidLog:0,broken:0,preChain:0};if(o.push(`Log integrity \xB7 ${a.logs} log(s): hash chain intact on ${u.intact} from seq 1 and ${u.intactFromMidLog} from mid-log, broken on ${u.broken}, ${u.preChain} pre-chain (written before the chain); ${a.headMissing} missing their head, ${a.withGaps} with seq gaps, ${a.withRepeats} with repeated seq`),o.push(" (the chain is walked over this node's copy \u2014 advisory; the plane's verified head is the anchor)"),o.push(""),!e.findings.length)o.push("Findings: none.");else{o.push(`Findings \xB7 ${e.findings.length}`);for(let l of e.findings.slice(0,t)){let i=l.to-l.from>B?`${_e(l.from)} \u2192 ${_e(l.to).slice(11)}`:_e(l.from),d=l.actions?`${l.count} prompt(s), ${l.actions} action(s)`:`${l.count}`;o.push(` [${l.severity}] ${l.rule} \xB7 ${l.track||l.trackId}${l.boxId?` \xB7 ${l.boxId}`:""} \xB7 ${i} \xB7 ${d}`),o.push(` ${l.note}`);for(let m of l.samples)o.push(` - ${m}`)}e.findings.length>t&&o.push(` \u2026 ${e.findings.length-t} more (--json for all)`)}return o.push(""),o.push(e.ok?`No action without an origin. ${n||"The log is agent-writable, so this is evidence, not proof."}`:`Actions with NO origin were found \u2014 read the [high] findings above.${n?` ${n}`:""}`),o.join(`
|
|
3
|
-
`)}import X from"node:fs";import at from"node:fs/promises";import re from"node:os";import A from"node:path";import{execFile as ct}from"node:child_process";import{promisify as
|
|
4
|
-
`).filter(t=>t&&!t.startsWith("#")).map(t=>t.split(":"))}catch{return[]}}function pt(e,t=process.env){let o=dt(t.BOSTRAT_PASSWD_FILE||"/etc/passwd").find(s=>s[0]===String(e)||s[2]===String(e));return o?{name:o[0],uid:Number(o[2]),gid:Number(o[3]),home:o[5]||null,shell:o[6]||null}:null}function ft(e,t=process.env){let n=dt(t.BOSTRAT_GROUP_FILE||"/etc/group"),o=new Set;for(let s of n)Number(s[2])===e.gid&&o.add(s[0]),(s[3]||"").split(",").includes(e.name)&&o.add(s[0]);return[...o].sort()}var se=()=>typeof process.getuid=="function"?process.getuid():0,Oe=null;function ie(e=process.env){let t=String(e[rt]||"").trim();if(
|
|
5
|
-
`)[0].slice(0,160);async function jn(){try{return await Ne("sudo",["-n","true"],{timeout:3e3}),!0}catch{return!1}}async function Dn(e=process.env,{ask:t=!0}={}){let n=e.BOSTRAT_AUDIT_LOG||"/var/log/audit/audit.log",o=!1;try{await at.access(n,X.constants.R_OK),o=!0}catch{}let s=e.BOSTRAT_AUDIT_RULES_FILE||"/etc/audit/rules.d/bostrat-box.rules",r=X.existsSync(s),a=null;if(t&&r){let u=se()===0?["auditctl",["-l"]]:["sudo",["-n","auditctl","-l"]];try{let{stdout:l}=await Ne(u[0],u[1],{timeout:5e3});a=l.includes(`key=${xn}`)}catch{a=null}}else r||(a=!1);return{log:n,readable:o,configured:r,rule:a,rulesFile:s}}function mt(e){return e.level==="uid"&&!e.problems.length?`Boxes on this node run as ${e.user}, which holds no route to root and cannot write the event log or its hook settings${e.auditd?.rule===!0&&e.auditd?.readable?"; its every exec is logged by auditd and shipped to the plane":""}.`:e.level==="uid"?`Boxes on this node run as ${e.user}, but containment has gaps (${e.problems.join("; ")}) \u2014 treat this record as evidence, not proof.`:e.level==="root-capable"?`Boxes on this node are root-capable (${e.user}: ${e.routes.join(", ")}), so a root-capable agent can defeat node-side evidence \u2014 this is evidence, not proof.`:e.level==="misconfigured"?`Box containment is configured but unusable (${e.error}); no box can start until it is fixed.`:`Boxes on this node share the node's uid (${e.user}), so an agent can rewrite its own log \u2014 this is evidence, not proof.`}var Pn="BP-AU-R4",xt=160,gt={high:0,medium:1,low:2},yt=200,bt=5,wt=8,St=5,kt=10*6e4,Mn=Object.freeze(["localhost","127.0.0.1","0.0.0.0","github.com","api.github.com","raw.githubusercontent.com","objects.githubusercontent.com","registry.npmjs.org","pypi.org","files.pythonhosted.org"]),Cn=5e3,Pe=new Set(["tool.called","tool.failed"]),je=new Set(["Write","Edit","NotebookEdit"]),Ln=e=>String(e??"").replace(/\s+/g," ").trim(),L=(e,t=120)=>Ln(e).slice(0,t);function Un(e,t=xt){let n=String(e??""),o=n.split(/\s*(?:;|&&|\|\||\||\n)\s*/).map(s=>s.trim()).filter(Boolean);return Number.isFinite(t)&&n.length>=t&&o.length&&o.pop(),o}function ae(e){return String(e??"").replace(/\d?>>?\s*\S+|<\s*\S+|2>&1/g," ").trim().split(/\s+/).filter(Boolean)}function Tt(e){let t=0;for(;t<e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[t]);)t++;return{cmd:e[t]??null,rest:e.slice(t+1),at:t}}function Et(e){let t=ae(e),n=t.findIndex(i=>i==="git"||i.endsWith("/git"));if(n<0)return null;for(n++;n<t.length&&t[n].startsWith("-");)(t[n]==="-C"||t[n]==="-c")&&n++,n++;if(t[n]!=="push")return null;let o=[],s=[];for(let i=n+1;i<t.length;i++){let d=t[i];d.startsWith("-")?(o.push(d),(d==="-o"||d==="--push-option"||d==="--repo"||d==="--receive-pack"||d==="--exec")&&i++):s.push(d)}let r=o.some(i=>i==="--force"||/^-[a-zA-Z]+$/.test(i)&&i.includes("f"))||s.some(i=>i.startsWith("+")),a=o.some(i=>i.startsWith("--force-with-lease")||i.startsWith("--force-if-includes")),u=s[0]??null,l=s.slice(1).map(i=>i.replace(/^["']|["']$/g,"").replace(/^\+/,"")).map(i=>i.includes(":")?i.slice(i.indexOf(":")+1):i).filter(i=>i&&!i.startsWith("$"));return{force:r,lease:a,remote:u,dsts:l}}var Me=String.raw`(?:~|\$HOME|\$\{HOME\}|/home/[^\s/"']+|/root|/Users/[^\s/"']+)`,Hn=new RegExp(`${Me}/\\.ssh(?:/|\\b)`),Bn=new RegExp(`${Me}/(?:\\.claude\\.json\\b|\\.claude/\\.credentials\\.json|\\.codex/auth\\.json|\\.cursor(?:/|\\b))`),Fn=/(?:^|[\s/"'=(])(\.env(?:\.[A-Za-z0-9_.-]+)?)(?=$|[\s"'|;&)])/g,Wn=/^\.env\.(?:example|sample|template|dist)$/,At=/(\.claude\/settings(?:\.local)?\.json|\.claude\/hooks\/[^\s"']*|\.bostrat\/events(?:\/[^\s"']*)?)/,It=new RegExp(`((?:/etc|/usr/lib|/lib|/run)/systemd/[^\\s"']*|${Me}/\\.config/systemd/[^\\s"']*|/etc/cron(?:tab|\\.d|\\.hourly|\\.daily|\\.weekly|\\.monthly)(?:/[^\\s"']*)?|/var/spool/cron(?:/[^\\s"']*)?)`),qn=/^(?:enable|disable|start|stop|restart|try-restart|reload|reload-or-restart|daemon-reload|daemon-reexec|link|mask|unmask|edit|preset|preset-all|set-property|revert|isolate|kill|reset-failed|import-environment|set-environment|add-wants|add-requires)$/,Kn=/^\/tmp\/|\/\.bostrat\/tmp\//,Gn=/^(?:refs\/heads\/)?(?:main|master|develop|development|trunk|production|prod|staging|release(?:\/|$)|v\d)/,Xn=/(?:^|\/)track\/|^HEAD$|^refs\/bostrat\//,Vn=/https?:\/\/([a-z0-9.-]+\.[a-z]{2,}|localhost|\d+\.\d+\.\d+\.\d+)(?::\d+)?/gi,$t=e=>Hn.test(e)||Bn.test(e),_t=e=>[...String(e??"").matchAll(Fn)].some(t=>!Wn.test(t[1])),zn=e=>At.test(e),Yn=e=>It.test(e),Jn=e=>(e.startsWith("/")||e.startsWith("~"))&&!Kn.test(e);function Ot(e){let{cmd:t,at:n}=Tt(e);if(t==="sudo"){for(n++;n<e.length&&e[n].startsWith("-");)n++;t=e[n]??null}return{cmd:t,at:n}}function Rt(e,t){let n=String(e??"").match(t);if(!n)return null;let o=n[1],s=o.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&");if(new RegExp(`>{1,2}\\s*["']?[^\\s"']*${s}`).test(e))return"redirect";let r=ae(e),{cmd:a}=Ot(r);return!a||!r.some(u=>u.includes(o))?null:/^(?:rm|mv|truncate|chmod|chown|tee|install|shred|unlink|ln|mkdir|touch)$/.test(a)?a:a==="sed"&&r.some(u=>/^-[a-zA-Z]*i/.test(u))?"sed -i":a==="cp"&&r[r.length-1].includes(o)?"cp":/^(?:python3?|perl|node|ruby)$/.test(a)&&/open\([^)]*['"][wa]|writeFile|write_text|appendFile|fs\.write/.test(e)?`${a} write`:null}var Zn=e=>Rt(e,At);function Qn(e){let t=ae(e),{cmd:n,at:o}=Ot(t);if(!n)return null;let s=t.slice(o+1);if(n==="crontab"||n.endsWith("/crontab")){let a=!1,u=!1;for(let l=0;l<s.length;l++){if(s[l]==="-u"){l++;continue}s[l]==="-l"||/^-(?:h|V|T|-help|-version)$/.test(s[l])?a=!0:u=!0}return u||!a?"crontab":null}if(n==="systemctl"||n.endsWith("/systemctl")){let a=s.find(u=>!u.startsWith("-"));return a&&qn.test(a)?`systemctl ${a}`:null}if((n==="loginctl"||n.endsWith("/loginctl"))&&s.includes("enable-linger"))return"loginctl enable-linger";if(n==="at"&&s.length&&/^(?:now|midnight|noon|teatime|today|tomorrow|next|\d|-[fqmvt])/.test(s[0]))return"at";let r=Rt(e,It);return r?`${r} unit/cron path`:null}function eo(e){let t=new Set;for(let n of String(e??"").matchAll(Vn))t.add(n[1].toLowerCase());return[...t]}function to({knownHosts:e=[]}={}){return{known:new Set([...Mn,...[...e].map(t=>String(t).toLowerCase()).filter(Boolean)]),hosts:new Set,tracks:new Map}}function no(e,t){let n=e.tracks.get(t??"");return n||e.tracks.set(t??"",n={costs:[],peer:new Map}),n}var I=(e,t,n,o)=>Object.freeze({severity:e,requirement:Pn,note:t,measured:n,detect:o}),Q=(e,t)=>{let n=0,o=null;for(let s of e.calls)if(s.head)for(let r of Un(s.head,e.headCap)){let a=t(r,s);a&&(n++,o??=L(a))}return n?{count:n,sample:o}:null},De=(e,t)=>{let n=0,o=null;for(let s of e.calls){let r=t(s);r&&(n++,o??=L(r))}return n?{count:n,sample:o}:null},V=Object.freeze({"force-push":I("high","git push --force / -f / +refspec in a command head (--force-with-lease, the Track's own rebase flow, is not one)","2026-10-21 fleet replay \xB7 0 hits / 98,690 hook calls \xB7 1,080 logs 2026-08\u219210 \xB7 58 --force-with-lease pushes excluded by design \u2192 high",(e,t)=>Q(t,n=>{let o=Et(n);return o&&o.force&&!o.lease?n:null})),"protected-config-write":I("high","a write to the box's own harness: .claude/settings*.json, a .claude/hooks/ script or the Track's .bostrat/events/ log \u2014 by a file tool, a redirect, rm/mv/tee/sed -i, or a scripted open-for-write (reads are not hits)","2026-10-21 fleet replay \xB7 0 hits / 98,690 hook calls \xB7 165 reads of those paths (cat, grep, python json.loads) excluded by design \u2192 high",(e,t)=>{let n=0,o=null;for(let r of t.calls)je.has(r.tool)&&r.path&&zn(r.path)&&(n++,o??=L(`${r.tool} ${r.path}`));let s=Q(t,r=>{let a=Zn(r);return a?`${a}: ${r}`:null});return s&&(n+=s.count,o??=s.sample),n?{count:n,sample:o}:null}),"persistence-install":I("medium","a shell segment that installs something the OS runs on its own \u2014 crontab (install / edit / remove; -l is a read), a mutating systemctl verb (enable / start / restart / daemon-reload \u2026; status / show / cat / list / is-* are reads), loginctl enable-linger, at / batch \u2014 or a write (file tool, redirect, cp / mv / tee / ln / sed -i) into a systemd unit or cron directory: how a process outlives its turn by the OS's hand (BP-AU-D6's other half)","2026-10-21 fleet replay \xB7 11 hits / 99,122 hook calls \xB7 10 Tracks \xB7 0 in the last 14 d \xB7 every hit operator-asked service work on the operator's own Pi (systemctl --user restart/stop/start of bostrat-node, tmp-sweep, a samba disable, one `crontab -` install for a backfill) \xB7 the bare `at` word a quoted `|` regex leaves in a segment (4 hits) excluded by design \u2192 medium (rail), the guard's deny when opted in",(e,t)=>{let n=0,o=null;for(let r of t.calls)je.has(r.tool)&&r.path&&Yn(r.path)&&(n++,o??=L(`${r.tool} ${r.path}`));let s=Q(t,r=>{let a=Qn(r);return a?`${a}: ${r}`:null});return s&&(n+=s.count,o??=s.sample),n?{count:n,sample:o}:null}),"push-outside-track":I("medium","git push to a protected branch (main/master/develop/release\u2026), to a branch that is not a Track branch, or to a remote other than origin/upstream \u2014 a Track's own code leaves on its track/ branch or the WIP shadow ref","2026-10-21 fleet replay \xB7 7 hits / 98,690 hook calls \xB7 4 Tracks \xB7 0 in the last 14 d \xB7 every hit operator-instructed work on another repo (two dep/ branches, a fork's multiplayer branch, one HEAD:main) \u2192 medium, not a page",(e,t)=>Q(t,n=>{let o=Et(n);return o&&(o.remote&&!/^(?:origin|upstream)$/.test(o.remote)&&!o.remote.startsWith("$")&&!o.remote.startsWith("-")||o.dsts.some(s=>Gn.test(s)||!Xn.test(s)))?n:null})),"credential-read":I("medium","a command head or file path naming SSH material (~/.ssh) or a vendor auth file (~/.claude.json, ~/.claude/.credentials.json, ~/.codex/auth.json, ~/.cursor) \u2014 the box-auth policy's denylist shapes; ~/.claude/projects (transcripts) is not one","2026-10-21 fleet replay \xB7 12 hits / 98,690 hook calls \xB7 8 Tracks \xB7 3 in the last 14 d \xB7 every hit an operator-asked inspection on the operator's own node (ssh config for a host alias, stat/keys of ~/.claude.json) \u2192 medium until a 14-day window reads 0",(e,t)=>De(t,n=>n.head&&$t(n.head)?n.head:n.path&&$t(n.path)?`${n.tool} ${n.path}`:null)),"dotenv-read":I("low","a command head or file path naming a .env file (.env, .env.local, .env.production \u2026; the committed .env.example / .sample / .template are not ones)","2026-10-21 fleet replay \xB7 309 hits / 98,690 hook calls \xB7 44 Tracks \xB7 0 in the last 14 d \xB7 all of them an instructed `source \u2026/almanac/.env` on the operator's other repos \u2192 low (report only)",(e,t)=>De(t,n=>n.head&&_t(n.head)?n.head:n.path&&_t(n.path)?`${n.tool} ${n.path}`:null)),"privileged-command":I("medium","a shell segment whose command is sudo or docker (after any VAR=value prefix) \u2014 host-level reach from inside a box","2026-10-21 fleet replay \xB7 220 hits (183 events) / 98,690 hook calls \xB7 57 Tracks \xB7 3 in the last 14 d \xB7 sudo = the operator's own journalctl/systemctl asks on the Pi, docker = instructed compose/psql work on the operator's other services \u2192 medium (a hosted node's first hit is the number to watch)",(e,t)=>Q(t,n=>{let{cmd:o}=Tt(ae(n));return o==="sudo"||o==="docker"?n:null})),"write-outside-workspace":I("low","a file tool (Write / Edit / NotebookEdit) on an absolute path \u2014 the hook strips the box's workspace prefix, so an absolute path is outside it \u2014 that is not the box's scratch dir (/tmp/\u2026, \u2026/.bostrat/tmp/\u2026)","2026-10-21 fleet replay \xB7 106 hits / 98,690 hook calls \xB7 10 Tracks \xB7 44 in the last 14 d \xB7 the operator's other repos and Agent-tool worktrees under .bostrat/repos on the operator's own node \u2192 low (report only)",(e,t)=>De(t,n=>je.has(n.tool)&&n.path&&Jn(n.path)?`${n.tool} ${n.path}`:null)),"egress-new-host":I("low","a command head reaching, by URL, a host no box in the fleet has reached before (this projection's lifetime, seeded from the warm tails; KNOWN_HOSTS + PLANE_AUDIT_KNOWN_HOSTS never count) \u2014 a baseline-growth signal, so each host is reported once","2026-10-21 fleet replay \xB7 140 first sightings / 98,690 hook calls \xB7 69 Tracks \xB7 8 in the last 14 d \xB7 198 if the baseline were per Track \xB7 every host a vendor API, a docs site or the operator's own services \u2192 low (report only)",(e,t)=>{let n=0,o=null;for(let s of t.calls)if(s.head)for(let r of eo(s.head))t.state.known.has(r)||t.state.hosts.has(r)||(t.state.hosts.add(r),t.state.hosts.size>Cn&&t.state.hosts.delete(t.state.hosts.values().next().value),n++,o??=L(`${r} \xB7 ${s.head}`));return n?{count:n,sample:o}:null}),"spend-spike":I("low",`a cost.recorded turn over ${wt}\xD7 the Track's running median (its last ${yt} cost events, at least ${bt} of them) and over $${St} \u2014 a turn that cost far more than this Track's turns do`,"2026-10-21 fleet replay \xB7 114 hits / 8,421 cost events \xB7 64 Tracks \xB7 2 in the last 14 d \xB7 every hit a long build turn after short prompts (a $25\u201378 turn on a $0.37 median) \u2014 real spend, no one's attack \u2192 low (report only)",(e,t)=>{if(e.type!=="cost.recorded")return null;let n=t.track.costs,o=Number(e.payload?.usd);if(!Number.isFinite(o)||o<0)return null;let s=null;if(n.length>=bt&&o>=St){let r=[...n].sort((u,l)=>u-l),a=r[Math.floor(r.length/2)];o>wt*a&&(s={count:1,sample:`$${o.toFixed(2)} vs median $${a.toFixed(2)} over ${n.length} turns${e.payload?.model?` \xB7 ${e.payload.model}`:""}`})}return n.push(o),n.length>yt&&n.shift(),s}),"peer-injected-call":I("medium",`a tool call within ${kt/6e4} min of a peer.message on the same box with no operator or node-typed turn in between \u2014 the cross-session injection vector (peer-message.js); a peer is never a root (BP-AU-D1)`,"2026-10-21 fleet replay \xB7 0 peer.message events in 288,395 \xB7 the replay cannot bound this rule's rate, so it stays medium (rail) until a window with peer traffic has been measured; the shape itself is BP-AU-D1's stated R4 case",(e,t)=>{if(!t.calls.length)return null;let n=t.track.peer.get(t.boxKey);if(n==null||t.ts-n>kt||t.ts<n)return null;let o=t.calls[0];return{count:t.count,sample:L(`${Math.round((t.ts-n)/1e3)}s after a peer message \xB7 ${o.tool}${o.head?`: ${o.head}`:o.path?` ${o.path}`:""}`)}}),"foreign-session-call":I("medium","a hook-recorded tool call from a claude session that is not the box's own (payload.foreignSession, BP-AU-I12): a nested or stray CLI driving the box's hooks \u2014 R3 never roots it, this names it","2026-10-21 fleet replay \xB7 14 hits / 98,744 hook calls \xB7 3 Tracks \xB7 4 in the last 14 d \xB7 all the xscout loop's nested claude prompts, I12's own acceptance case \u2192 medium (rail), not a page",(e,t)=>{if(!t.calls.length||!e.payload?.foreignSession)return null;let n=t.calls[0];return{count:t.count,sample:L(`session ${String(e.payload.sessionId||"?").slice(0,8)} \xB7 ${n.tool}${n.head?`: ${n.head}`:n.path?` ${n.path}`:""}`)}}),"orphan-process":I("medium","a process the box left running: detached from the pane's tree (its cgroup or pty still lists it), alive at a turn's close and still alive minutes later with no turn open and no background job recorded \u2014 the node's own census (orphan-watch.js, BP-AU-D6), each process once","2026-10-27 fleet replay \xB7 0 audit.procs events in 304,814 (1,121 logs; the type is new, so the replay cannot bound this rule's rate) \xB7 live Pi census the same day: 10 boxes, 2 detached processes, both found by the cgroup census and neither by the pty one (a claude tool call runs on pipes) \u2014 one planted `nohup sleep &`, one real `flyctl agent run` that a lead's flyctl call had left behind 10 h earlier \u2192 medium (rail), never a page, until a window of live samples has been measured",e=>{if(e.type!=="audit.procs"||e.source!=="proc")return null;let t=e.payload||{},n=Array.isArray(t.orphans)?t.orphans:[],o=n.length+(Number(t.dropped)||0);if(!o)return null;let s=n[0]||{},r=Number.isFinite(s.sinceMs)?` \xB7 alive ${Math.max(1,Math.round(s.sinceMs/6e4))} min`:"";return{count:o,sample:L(`${s.comm||"process"}${s.cmd?`: ${s.cmd}`:""}${s.via?` [${s.via}]`:""}${r}${o>1?` (+${o-1} more)`:""}`)}}),"unknown-harness-shape":I("medium","a harness-made user turn of a shape chat-notes.js does not list (harness.shape.seen with known: false, harness-shapes.js): Claude Code wrote a new kind of its own turn into a transcript \u2014 hidden from chat by the generic catch, counted once per shape per node here; the node pages the operator once on first sight, this row is the fleet's record","2026-10-28 fleet replay \xB7 0 harness.shape.seen events in 311,055 (1,132 logs; the type is new, so the replay cannot bound this rule's rate) \xB7 transcript scan the same day: 2,863 claude user turns in 1,539 transcripts over 90 d, 144 of them harness-made (isMeta), 9 distinct shapes, 4 of which had rendered in chat as operator messages (<system-reminder><tools-used-before-summary> on 2 Tracks under 2.1.312, a usage-limit-reset notice, a safety-classifier notice) \u2192 medium (rail finding + security row), never a page from the plane: the once-per-shape page is the node's, so a release that adds one shape pages once, not per Track per hour",e=>{if(e.type!=="harness.shape.seen"||e.source!=="transcript")return null;let t=e.payload||{};if(t.known!==!1||!t.key)return null;let n=`${t.provider||"cli"}${t.cliVersion?` ${t.cliVersion}`:""}`;return{count:1,sample:L(`${t.key} \xB7 ${n}${t.head?` \xB7 ${t.head}`:""}`)}})}),z=Object.freeze(Object.keys(V)),oo=new Set([...Pe,"cost.recorded","peer.message","turn.user","audit.procs","harness.shape.seen"]);function so(e,t,{rules:n=null,headCap:o=xt}={}){if(!t||!oo.has(t.type)||t.source==="plane")return[];let s=no(e,t.trackId),r=t.payload||{},a=t.boxId??"",u=Number(t.ts)||0;if(t.type==="turn.user")return(t.actor==="user"||t.actor==="orchestrator")&&s.peer.delete(a),[];if(t.type==="peer.message")return s.peer.set(a,u),[];let l=Pe.has(t.type)&&t.source==="hook",d={calls:l?P(r):[],count:l?q(r):0,track:s,state:e,ts:u,boxKey:a,headCap:o},m=[];for(let h of n||z){let g=V[h];if(!g)continue;let S=null;try{S=g.detect(t,d)}catch{S=null}S&&S.count>0&&m.push({rule:h,count:S.count,sample:S.sample??null})}return m}function vt(e,{days:t=14,now:n=Date.now(),rules:o=null}={}){let s=t?n-t*864e5:0,r=to(),a=o||z,u={days:t||null,logs:0,events:0,calls:0,callsInWindow:0,rules:Object.fromEntries(a.map(i=>[i,{severity:V[i].severity,note:V[i].note,measured:V[i].measured,hits:0,hitsInWindow:0,events:0,tracks:0,tracksInWindow:0,perTenK:0,samples:[]}]))},l=Object.fromEntries(a.map(i=>[i,{all:new Set,win:new Set}]));for(let i of e||[]){let d=Array.isArray(i?.events)?[...i.events].sort((m,h)=>(Number(m.ts)||0)-(Number(h.ts)||0)):[];if(d.length){u.logs++;for(let m of d){u.events++;let h=(Number(m.ts)||0)>=s;if(Pe.has(m.type)&&m.source==="hook"){let g=q(m.payload||{});u.calls+=g,h&&(u.callsInWindow+=g)}for(let g of so(r,m,{rules:a})){let S=u.rules[g.rule];if(S.hits+=g.count,S.events++,l[g.rule].all.add(m.trackId??i.trackId),h&&(S.hitsInWindow+=g.count,l[g.rule].win.add(m.trackId??i.trackId)),g.sample){let j=S.samples.find(b=>b.text===g.sample);j?j.n++:S.samples.length<200&&S.samples.push({text:g.sample,n:1,ts:Number(m.ts)||0,trackId:m.trackId??i.trackId,name:i.name??null})}}}}}for(let i of a){let d=u.rules[i];d.tracks=l[i].all.size,d.tracksInWindow=l[i].win.size,d.perTenK=u.calls?Number((d.hits/u.calls*1e4).toFixed(2)):0,d.samples.sort((m,h)=>h.n-m.n||h.ts-m.ts),d.samples=d.samples.slice(0,50)}return u.ok=!a.some(i=>V[i].severity==="high"&&u.rules[i].hitsInWindow>0),u}var ro=e=>new Date(e).toISOString().slice(0,10);function Nt(e,{samples:t=5}={}){let n=[];n.push(`R4 DETECTOR REPLAY \xB7 ${e.days?`window last ${e.days}d`:"all history"} \xB7 ${e.logs} logs \xB7 ${e.events.toLocaleString()} events \xB7 ${e.calls.toLocaleString()} hook calls (${e.callsInWindow.toLocaleString()} in window)`),n.push("A rule's severity is what its measured rate supports; `high` pages (D7). Hits are what the plane fold would have flagged.");let o=Object.keys(e.rules).sort((s,r)=>gt[e.rules[s].severity]-gt[e.rules[r].severity]||s.localeCompare(r));for(let s of o){let r=e.rules[s];n.push(""),n.push(`${s} [${r.severity}] \xB7 ${r.hits} hit(s) all-time (${r.events} events, ${r.tracks} Tracks, ${r.perTenK}/10k calls) \xB7 ${r.hitsInWindow} in window (${r.tracksInWindow} Tracks)`),n.push(` ${r.note}`),r.measured&&n.push(` measured: ${r.measured}`);for(let a of r.samples.slice(0,t))n.push(` ${a.n}\xD7 ${ro(a.ts)} ${a.name?`${a.name} `:""}(${a.trackId}) ${a.text}`)}return n.push(""),n.push(e.ok?"ok: no high-severity detector hit in the window":"ATTENTION: a high-severity detector hit inside the window (exit 2)"),n.join(`
|
|
6
|
-
`)}import Ft from"node:fs/promises";import
|
|
7
|
-
`)}catch(d){throw te.set(n,r-1),pe.set(n,a),d}return Be.emit("event",i),Be.emit(`track:${n}`,i),i})}var
|
|
8
|
-
`))if(n.trim())try{t.push(JSON.parse(fe(n)?he(n):n))}catch{}return t}import me from"node:fs/promises";import
|
|
9
|
-
`,n!=null?{mode:n}:void 0),await me.rename(o,e)}catch(s){throw await me.unlink(o).catch(()=>{}),s}}var
|
|
10
|
-
`)}var
|
|
2
|
+
import ye from"node:fs/promises";import Bo from"node:os";import K from"node:path";var Vt=(e,t)=>{if(e==null||e==="")return t;let n=Number(e);return Number.isFinite(n)&&n>=0?n:t},qo=Vt(process.env.BOSTRAT_HOOK_FOLD_MS,250);var zt=1e3;function q(e){let t=Number(e?.count);return Number.isInteger(t)&&t>1&&e?.tools&&typeof e.tools=="object"?Math.min(t,zt):1}function P(e){let t=e||{},n=q(t);if(n===1)return[t];let o=(Array.isArray(t.calls)?t.calls:[]).filter(r=>r&&typeof r=="object").slice(0,n),s={};for(let r of o)r.tool&&(s[r.tool]=(s[r.tool]||0)+1);for(let[r,a]of Object.entries(t.tools))for(let u=s[r]||0;u<Number(a)&&o.length<n;u++)o.push({tool:r});for(;o.length<n;)o.push({tool:null});return o}import{createHash as Yt}from"node:crypto";var Jt=new Set(["userId"]);function ke(e){return e===null||typeof e!="object"?e===void 0||typeof e=="number"&&!Number.isFinite(e)?"null":JSON.stringify(e):Array.isArray(e)?`[${e.map(n=>ke(n===void 0?null:n)).join(",")}]`:`{${Object.keys(e).filter(n=>e[n]!==void 0).sort().map(n=>`${JSON.stringify(n)}:${ke(e[n])}`).join(",")}}`}function oe(e){let t={};for(let n of Object.keys(e))Jt.has(n)||(t[n]=e[n]);return Yt("sha256").update(ke(t)).digest("hex")}var Ee=e=>Number.isFinite(e?.seq);function Ye(e){let t={members:0,chained:0,preChain:0,intactFrom:null,intactTo:null,genesis:!1,breaks:[]},n=null,o=!1,s=(r,a)=>{n={seq:r.seq,hash:a},t.intactFrom=r.seq,t.genesis=r.seq===1&&r.prev===null};for(let r of e||[]){if(!Ee(r))continue;if(t.members++,r.prev===void 0){n&&!o?(t.breaks.push({kind:"unchained",seq:r.seq,eventId:r.id??null}),o=!0):n||t.preChain++;continue}t.chained++;let a=oe(r);if(!n||o){o=!1,s(r,a);continue}if(r.seq===n.seq+1){if(r.prev===n.hash){n={seq:r.seq,hash:a};continue}t.breaks.push({kind:"prev-mismatch",seq:r.seq,eventId:r.id??null,expected:n.hash,got:r.prev})}else r.seq>n.seq+1?t.breaks.push({kind:"seq-gap",seq:r.seq,eventId:r.id??null,from:n.seq+1,to:r.seq-1}):t.breaks.push({kind:"seq-repeat",seq:r.seq,eventId:r.id??null,headSeq:n.seq});s(r,a)}return t.intactTo=n?n.seq:null,n||(t.intactFrom=null),t}var B=6e4,Zt=120*B,Je=2e3,Ze=5e3,Qt=2*B,en=5e3,tn=2*B,Z=5e3,nn={Workflow:360*B,Agent:60*B},on=5*B,sn=10*B,xe=80,M=3,rn=["Checkpoint: write a work-in-progress handoff","Resume from .bostrat/handoff/latest.md","Beat: write a","You are a SUBTASK agent working in an isolated","A subtask box you delegated has finished"],Ae={operator:"strong",orchestrator:"strong","card-answer":"strong",loop:"strong","background-job":"strong","task-continuation":"strong","operator-terminal":"weak","orchestrator-unlogged":"weak","background-launch":"weak","task-continuation-unlogged":"weak",peer:"flagged","nested-session":"flagged","unknown-prompt":"flagged",unrooted:"flagged"},tt={claude:{source:"hook",note:"hook-sourced: the CLI's hooks record the turn start (prompt_id), each call's intent and outcome (tool_use_id), failed calls and the Stop; the transcript is the duplicate"},codex:{source:"transcript",note:"transcript-sourced (a file the agent writes): the rollout names each turn (turn_id, turn_trigger, parent_turn_id) and each call (call_id), so a call cites the send that opened its turn; no intent before a call, no failed-call record, no turn end on the log, and a prompt typed at the pane is not on the log"},cursor:{source:"transcript",note:"transcript-sourced (a store the agent writes): message order is the turn structure, so a call cites the user message that opened its turn (its content hash); no turn ids, no per-message time (ts is synthesized), no intent, no failed-call record, no turn end, and a prompt typed at the pane is not on the log"}},C=e=>String(e??"").replace(/\s+/g," ").trim(),y=e=>e?.ts??0,Ie=(e,t)=>!!e&&!!t&&(e.startsWith(t.slice(0,xe))||t.startsWith(e.slice(0,xe))),an=6,cn=(e,t)=>Ie(e,t)||e.length>=an&&t.includes(e.slice(0,xe)),ln=/^<pasted_content\b[^>]*>\s*/i,Te=e=>C(e.payload?.promptHead??e.payload?.text).replace(ln,""),un=/^<cross-session-message\b/i,dn=/^<task-notification>/i,pn=/<tool-use-id>\s*([^<\s]+)\s*<\/tool-use-id>/i,fn=/<task-id>\s*([^<\s]+)\s*<\/task-id>/i;function $e(e){let t=e.payload||{},n=q(t),o=P(t)[0],s=o.head||o.path||(t.command?C(t.command):""),r=e.type==="tool.failed"?" [failed]":e.type==="tool.started"?" [never completed]":"";return`${o.tool||"tool"}${s?`: ${s.slice(0,100)}`:""}${r}${n>1?` (+${n-1} more)`:""}`}var hn=/\.answered$/,mn=new Set(["loop.created","followup.created"]),gn=16,nt=new Set(["tool.started","tool.called","tool.failed"]);function yn(){return{byId:new Map,byDelivery:new Map,byPrompt:new Map,byToolUse:new Map,jobs:new Map}}function bn(e,t){if(!t||typeof t.type!="string")return;t.id&&e.byId.set(t.id,t);let n=t.payload||{};if(t.type==="turn.user")t.source==="orchestrator"&&n.deliveryId&&e.byDelivery.set(n.deliveryId,t),t.source==="hook"&&n.promptId&&!e.byPrompt.has(`${t.boxId}\0${n.promptId}`)&&e.byPrompt.set(`${t.boxId}\0${n.promptId}`,t);else if(t.type==="job.started"&&n.id)e.jobs.set(n.id,t),n.taskId&&e.jobs.set(n.taskId,t);else if(nt.has(t.type)&&t.source==="hook")for(let o of P(n))o.toolUseId&&!e.byToolUse.has(o.toolUseId)&&e.byToolUse.set(o.toolUseId,t)}function wn(e){let t=yn();for(let n of e)bn(t,n);return t}function Qe(e,t){let n=e;for(let o=0;n&&o<gn;o++){let s=n.payload||{},r=n.type;if(n.causationId&&t.byId.has(n.causationId)&&n.causationId!==n.id){n=t.byId.get(n.causationId);continue}if(nt.has(r)){let a=s.promptId?t.byPrompt.get(`${n.boxId}\0${s.promptId}`):null;if(!a)return null;n=a;continue}if(r==="turn.user"){if(n.source==="hook"){if(s.kind==="task-notification"&&s.jobId){let u=t.jobs.get(s.jobId);if(!u)return null;n=u;continue}let a=s.deliveryId?t.byDelivery.get(s.deliveryId):null;if(!a)return null;n=a;continue}return n.source==="orchestrator"?s.loopId||s.producer==="loop"?{origin:"loop",root:n}:{origin:n.actor==="orchestrator"?"orchestrator":"operator",root:n}:n.source==="transcript"?{origin:"operator-terminal",root:n}:null}if(r==="job.started"){let a=s.id?t.byToolUse.get(s.id):null;if(!a)return null;n=a;continue}return hn.test(r)||mn.has(r)?{origin:"card-answer",root:n}:r==="peer.message"?{origin:"peer",root:n}:null}return null}function Sn(e){let t={events:e.length,firstSeq:null,lastSeq:null,headMissing:!1,gaps:0,repeats:0},n=null;for(let s of e)Number.isFinite(s?.seq)&&(n===null?t.firstSeq=s.seq:s.seq>n+1?t.gaps++:s.seq<=n&&t.repeats++,n=s.seq);t.lastSeq=n,t.headMissing=t.firstSeq!==null&&t.firstSeq>1;let o=Ye(e);return t.chain={state:o.chained===0?"pre-chain":o.breaks.length?"broken":o.genesis?"intact":"intact-from-mid-log",chained:o.chained,preChain:o.preChain,intactFrom:o.intactFrom,intactTo:o.intactTo,breaks:o.breaks},t}var kn=e=>{switch(e.kind){case"prev-mismatch":return`seq ${e.seq} states a predecessor that is not the line before it \u2014 the line before it changed, or was replaced`;case"seq-gap":return`seq ${e.from}${e.to!==e.from?`\u2013${e.to}`:""} missing between chained lines`;case"seq-repeat":return`seq ${e.seq} appears again after the chain passed it (a line was inserted, or the log was rewound)`;case"unchained":return`seq ${e.seq} carries no chain link after chained lines (an older node resumed this log)`;default:return`${e.kind} at seq ${e.seq}`}};function En(e){let t=new Map,n=new Map,o=new Map,s=new Map,r=(i,d,m)=>{i.has(d)||i.set(d,[]),i.get(d).push(m)};for(let i of e)if((i.type==="box.state.changed"||i.type.startsWith("handoff."))&&r(s,i.payload?.box||i.boxId||null,y(i)),i.type==="session.ready"&&([...s.get(i.boxId)||[],...s.get(null)||[]].some(m=>m<=y(i)&&y(i)-m<=tn)||r(o,i.boxId,y(i))),i.type==="turn.user")if(i.source==="orchestrator"){let d=(i.payload?.attachments||[]).map(m=>m?.id).filter(Boolean);r(t,i.boxId,{ts:y(i),head:C(i.payload?.text),files:d,actor:i.actor,usedAt:null})}else i.source==="transcript"&&r(n,i.boxId,{ts:y(i),head:C(i.payload?.text),note:i.payload?.note?.kind||null});let a=new Map,u=new Map,l=new Map;for(let i of e){if(i.type==="turn.user"&&i.source==="orchestrator"&&u.set(i.boxId,!0),i.source!=="hook")continue;if(i.type==="turn.agent"){let h=l.get(i.boxId)||0;h>0?l.set(i.boxId,h-1):u.set(i.boxId,!1);continue}if(i.type!=="turn.user")continue;let d=Te(i),m=(()=>{if(i.actor==="peer"||un.test(d))return{origin:"peer"};if(dn.test(d))return{origin:"task-continuation",jobId:d.match(pn)?.[1]||d.match(fn)?.[1]||null};let h=(t.get(i.boxId)||[]).filter(b=>(b.usedAt===null||y(i)-b.usedAt<=Ze)&&b.ts>=y(i)-Zt&&b.ts<=y(i)+Je&&(cn(b.head,d)||!b.head&&b.files.length>0&&(b.files.some(x=>d.includes(`.bostrat/uploads/${x}`))||y(i)-b.ts<=Ze))),g=h[h.length-1];if(g)return g.usedAt=y(i),{origin:g.actor==="orchestrator"?"orchestrator":"operator"};if(rn.some(b=>d.startsWith(b)))return{origin:"orchestrator-unlogged"};if((n.get(i.boxId)||[]).find(b=>!b.note&&b.ts>=y(i)-Je&&b.ts<=y(i)+Qt&&Ie(b.head,d)))return{origin:"operator-terminal"};let j=(o.get(i.boxId)||[]).some(b=>b<=y(i)&&y(i)-b<=en);return{origin:u.get(i.boxId)||j?"nested-session":"unknown-prompt"}})();a.set(i,m),m.origin==="nested-session"?l.set(i.boxId,(l.get(i.boxId)||0)+1):u.set(i.boxId,!0)}return a}function $n(e){let t=new Map,n=new Map;for(let o of e){let s=o.payload?.id;if(s)if(o.type==="job.started"){let r={from:(o.payload?.ts??y(o))-Z,to:1/0,loggedAt:y(o)};n.set(s,r),o.payload?.taskId&&n.set(o.payload.taskId,r),t.has(o.boxId)||t.set(o.boxId,[]),t.get(o.boxId).push(r)}else o.type==="job.stopped"&&n.has(s)&&(n.get(s).to=y(o)+Z)}return{byBox:t,byId:n}}function _n(e,{trackId:t=null,name:n=null,since:o=0}={}){let s=En(e),r=$n(e),a=new Set;for(let c of e)c.type==="tool.called"&&c.source==="hook"&&a.add(c.boxId);let u=c=>c.type==="tool.called"&&(c.source==="hook"||!(c.provider==="claude"&&a.has(c.boxId)))||c.type==="tool.failed"&&c.source==="hook",l=new Set,i=new Map;for(let c of e)if(c.source==="hook")if(c.type==="tool.called"||c.type==="tool.failed")for(let p of P(c.payload))p.toolUseId&&l.add(p.toolUseId);else c.type==="audit.flagged"&&c.payload?.denied===!0&&c.payload.toolUseId?l.add(c.payload.toolUseId):(c.type==="turn.agent"||c.type==="session.ready"||c.type==="turn.aborted"||c.type==="turn.interrupted")&&(i.has(c.boxId)||i.set(c.boxId,[]),i.get(c.boxId).push(y(c)));let d=e.length?y(e[e.length-1]):0,m=c=>{if(c.type!=="tool.started"||c.source!=="hook")return null;let p=P(c.payload).filter($=>$.toolUseId&&!l.has($.toolUseId));return p.length&&((i.get(c.boxId)||[]).some($=>$>y(c))||d-y(c)>sn)?p:null},h={},g={},S={keyed:0,turn:0,timing:0},j=wn(e),b=new Map,x=(c,p,f,$,_,w=1)=>{let U=`${c}\0${f.boxId}\0${$}`,N=b.get(U);return(!N||y(f)-N.to>on)&&(N={rule:c,severity:p,trackId:t,track:n,boxId:f.boxId,from:y(f),to:y(f),count:0,note:_,samples:[]},b.set(U,N),T.push(N)),N.to=y(f),N.count+=w,N},T=[],v=new Map,k=c=>(v.has(c)||v.set(c,{open:!1,origin:null,nested:0,soft:null,lastSendHead:null,launch:null}),v.get(c));for(let c of e){let p=k(c.boxId),f=y(c)>=o;if(c.type==="session.foreign"){if(c.payload?.phase==="prompted"&&f){let E=x("nested-session","medium",c,"","a session that is not the box's own was prompted through this box \u2014 typically an agent-launched `claude -p`");E.samples.length<M&&E.samples.push(C(c.payload?.promptHead).slice(0,100))}continue}if(c.type==="turn.user"&&c.source==="hook"){let E=s.get(c);if(E.origin==="nested-session"){if(p.nested++,f){let W=x("nested-session","medium",c,"","a prompt no recorded send accounts for arrived mid-turn or right after a SessionStart \u2014 typically an agent-launched `claude -p`");W.samples.length<M&&W.samples.push(Te(c).slice(0,100))}continue}if(p.open=!0,p.origin=E.origin,E.origin==="task-continuation"&&!r.byId.has(E.jobId)&&(p.origin="task-continuation-unlogged"),p.origin==="unknown-prompt"&&f){let W=x("unknown-prompt","medium",c,"","a turn opened by a prompt with no operator, machinery or job record behind it");W.samples.length<M&&W.samples.push(Te(c).slice(0,100))}}else if(c.type==="turn.agent"&&c.source==="hook")p.nested>0?p.nested--:p.open=!1;else if(c.type==="turn.aborted"||c.type==="turn.interrupted")p.open=!1;else if(c.type==="turn.user"&&c.source==="orchestrator")p.lastSendHead=C(c.payload?.text),p.soft=c.actor==="orchestrator"?"orchestrator":"operator",p.open=!0,p.origin=p.soft;else if(c.type==="turn.user"&&c.source==="transcript"){let E=C(c.payload?.text);!c.payload?.note&&!Ie(E,p.lastSendHead)&&(p.soft="operator-terminal")}else c.type==="peer.message"&&(p.soft="peer");let $=m(c);if(!$&&!u(c))continue;let _=$?$.length:c.source==="hook"?q(c.payload):1,w;if(c.payload?.foreignSession)w="nested-session";else if(c.source==="hook"){let E=(r.byBox.get(c.boxId)||[]).some(H=>y(c)>=H.from&&y(c)<=H.to);p.open?w=p.origin:E?w="background-job":p.nested>0?w="nested-session":p.launch&&y(c)<=p.launch?w="background-launch":w="unrooted";let W=(r.byBox.get(c.boxId)||[]).some(H=>H.loggedAt<=y(c)+Z);for(let H of P(c.payload)){let Se=H.tool,Xt=!!H.toolUseId&&r.byId.has(H.toolUseId);(Se==="Workflow"&&!Xt||Se==="Agent"&&!W)&&p.open&&Ae[p.origin]!=="flagged"&&(p.launch=Math.max(p.launch||0,y(c)+nn[Se]))}}else c.payload?.turnId?w=String(c.payload.turnTrigger||"user")==="user"?"operator-terminal":"unknown-prompt":w=p.soft||"unrooted";let U=c.payload?.foreignSession?null:Qe(c,j);if(U&&(w=U.origin),!f)continue;let N=!U&&c.source!=="hook"&&!!c.payload?.turnId;S[U?"keyed":N?"turn":"timing"]+=_,h[w]=(h[w]||0)+_;let G=c.provider||"unknown";if(g[G]||={actions:0,fidelity:c.source==="hook"?"hook":"transcript",identified:!1,turnKeyed:!1,rooted:{key:0,turn:0,timing:0}},g[G].actions+=_,g[G].rooted[U?"key":N?"turn":"timing"]+=_,c.source==="hook"&&(g[G].fidelity="hook"),c.source==="hook"&&P(c.payload).some(E=>E.toolUseId)&&(g[G].identified=!0),c.source!=="hook"&&c.payload?.turnId&&(g[G].turnKeyed=!0),w==="unknown-prompt"&&c.source!=="hook"){let E=x("unexplained-turn","medium",c,String(c.payload.turnId),`calls in a transcript turn the CLI says no prompt opened (turn_trigger: ${String(c.payload.turnTrigger)}) and that cites no recorded cause`,_);E.samples.length<M&&E.samples.push($e(c));continue}if($){let E=x("unfinished-calls","low",c,"","calls the CLI began (PreToolUse) and never reported complete or failed \u2014 killed, crashed or restarted mid-call; the tool may have run",_);E.samples.length<M&&E.samples.push($e(c))}if(w==="unrooted"){let E=x("unrooted-actions","high",c,"","tool calls with no open turn, no recorded job and no launch that accounts for them",_);E.samples.length<M&&E.samples.push($e(c))}else if(w==="peer")x("peer-driven-actions","low",c,"","tool calls in a turn another session's message opened",_);else if(w==="nested-session"||w==="unknown-prompt"){let E=b.get(`${w}\0${c.boxId}\0`);E&&(E.actions=(E.actions||0)+_)}}let D={total:0,keyed:0,timing:0,unrooted:0};{let c=new Map,p=new Map;for(let f of e)f.source==="hook"&&(f.type==="turn.user"?p.set(f.boxId,y(f)):(f.type==="turn.agent"||f.type==="turn.aborted"||f.type==="turn.interrupted")&&p.has(f.boxId)&&((c.get(f.boxId)||c.set(f.boxId,[]).get(f.boxId)).push([p.get(f.boxId),y(f)]),p.delete(f.boxId)));for(let[f,$]of p)(c.get(f)||c.set(f,[]).get(f)).push([$,1/0]);for(let f of e){if(f.type!=="code.committed"||f.source!=="hook"||y(f)<o)continue;D.total++;let $=Number(f.payload?.ts??f.ts)||y(f);if(Qe(f,j))D.keyed++;else if((c.get(f.boxId)||[]).some(([_,w])=>$>=_-Z&&$<=w+Z))D.timing++;else{D.unrooted++;let _=x("unrooted-commit","medium",f,"","a commit landed in the box's workspace with no turn open and no recorded cause");_.samples.length<M&&_.samples.push(`${String(f.payload?.sha||"").slice(0,7)} ${C(f.payload?.subject).slice(0,80)}`)}}}{let c=e.filter(p=>p.type==="audit.flagged"&&p.source!=="plane"&&p.payload?.denied===!0&&y(p)>=o);if(c.length){let p=[...new Set(c.map(f=>f.payload?.rule).filter(Boolean))];T.push({rule:"guard-denied",severity:"high",trackId:t,track:n,boxId:c[0].boxId??null,from:y(c[0]),to:y(c.at(-1)),count:c.length,samples:c.slice(0,M).map(f=>`${f.payload?.rule||"?"}: ${C(f.payload?.samples?.[0]||f.payload?.tool||"")}`.slice(0,160)),note:`${c.length} call(s) denied before running by the node's PreToolUse guard (${p.join(", ")}) \u2014 BOSTRAT_AUDIT_GUARD is on for this node`})}}let O=Sn(e),F=Object.values(h).reduce((c,p)=>c+p,0);if(O.headMissing&&F&&T.push({rule:"log-head-missing",severity:"medium",trackId:t,track:n,boxId:null,from:y(e[0]),to:y(e[0]),count:1,samples:[],note:`the log starts at seq ${O.firstSeq} \u2014 its earlier history is not on this node`}),O.chain.breaks.length){let c=new Map;for(let w of e)Number.isFinite(w?.seq)&&!c.has(w.seq)&&c.set(w.seq,w);let p=O.chain.breaks.filter(w=>w.kind==="prev-mismatch"||w.kind==="seq-repeat"),f=w=>y(c.get(w.seq))||y(e.at(-1)),$=O.chain.breaks[0],_=O.chain.breaks.at(-1);T.push({rule:"chain-broken",severity:p.length?"high":"medium",trackId:t,track:n,boxId:null,from:f($),to:f(_),count:O.chain.breaks.length,samples:O.chain.breaks.slice(0,M).map(kn),note:`the log's hash chain breaks ${O.chain.breaks.length} time(s) on this node \u2014 advisory: the plane's verified head is the anchor (GET /plane/admin/chain/${t??"<track>"})`})}return{trackId:t,track:n,actions:F,byOrigin:h,byEvidence:S,byProvider:g,commits:D,findings:T,integrity:O}}var et={high:0,medium:1,low:2};function ot(e,{days:t=14,now:n=Date.now()}={}){let o=t?n-t*864e5:0,s={days:t||null,tracks:0,actions:0,byOrigin:{},byStrength:{strong:0,weak:0,flagged:0},byEvidence:{keyed:0,turn:0,timing:0},commits:{total:0,keyed:0,timing:0,unrooted:0},byProvider:{},findings:[],integrity:{logs:0,headMissing:0,withGaps:0,withRepeats:0,chain:{intact:0,intactFromMidLog:0,broken:0,preChain:0}}};for(let r of e||[]){let a=_n(r.events||[],{trackId:r.trackId,name:r.name,since:o});s.integrity.logs++,a.integrity.headMissing&&s.integrity.headMissing++,a.integrity.gaps&&s.integrity.withGaps++,a.integrity.repeats&&s.integrity.withRepeats++;let u=a.integrity.chain?.state;if(u==="intact"?s.integrity.chain.intact++:u==="intact-from-mid-log"?s.integrity.chain.intactFromMidLog++:u==="broken"?s.integrity.chain.broken++:s.integrity.chain.preChain++,!(!a.actions&&!a.findings.length)){s.tracks++,s.actions+=a.actions;for(let l of Object.keys(s.byEvidence))s.byEvidence[l]+=a.byEvidence?.[l]||0;for(let l of Object.keys(s.commits))s.commits[l]+=a.commits?.[l]||0;for(let[l,i]of Object.entries(a.byOrigin))s.byOrigin[l]=(s.byOrigin[l]||0)+i,s.byStrength[Ae[l]||"flagged"]+=i;for(let[l,i]of Object.entries(a.byProvider)){s.byProvider[l]||={actions:0,fidelity:i.fidelity,identified:!1,turnKeyed:!1,rooted:{key:0,turn:0,timing:0}},s.byProvider[l].actions+=i.actions,i.fidelity==="hook"&&(s.byProvider[l].fidelity="hook"),i.identified&&(s.byProvider[l].identified=!0),i.turnKeyed&&(s.byProvider[l].turnKeyed=!0);for(let d of["key","turn","timing"])s.byProvider[l].rooted[d]+=i.rooted?.[d]||0}s.findings.push(...a.findings)}}for(let[r,a]of Object.entries(s.byProvider))a.note=tt[r]?.note??(a.fidelity==="hook"?"hook-sourced":"transcript-sourced (a record the agent writes); no turn structure read by its adapter \u2014 calls attributed to the latest prompt");return s.findings.sort((r,a)=>et[r.severity]-et[a.severity]||a.to-r.to),s.ok=!s.findings.some(r=>r.severity==="high"),s}var J=(e,t)=>t?`${(e/t*100).toFixed(e&&e/t<.001?2:1)}%`:"0%",_e=e=>new Date(e).toISOString().slice(0,16).replace("T"," ");function st(e,{limit:t=20,containment:n=null}={}){let o=[];o.push(`AGENT ACTION AUDIT \xB7 ${e.days?`last ${e.days}d`:"all history"} \xB7 ${e.actions} action(s) across ${e.tracks} Track(s)`),o.push(""),o.push("Who asked for it");for(let l of["strong","weak","flagged"]){let i=Object.entries(e.byOrigin).filter(([d])=>(Ae[d]||"flagged")===l).sort((d,m)=>m[1]-d[1]);if(i.length){o.push(` ${l.toUpperCase()} \xB7 ${e.byStrength[l]} (${J(e.byStrength[l],e.actions)})`);for(let[d,m]of i)o.push(` ${d.padEnd(27)} ${String(m).padStart(7)} ${J(m,e.actions)}`)}}o.push("");let s=e.byEvidence||{keyed:0,turn:0,timing:0};o.push(`Rooted by recorded key \xB7 ${s.keyed} (${J(s.keyed,e.actions)})`+(s.turn?` \xB7 by its transcript turn only \xB7 ${s.turn} (${J(s.turn,e.actions)})`:"")+` \xB7 by timing \xB7 ${s.timing} (${J(s.timing,e.actions)})`);let r=e.commits||{total:0,keyed:0,timing:0,unrooted:0};r.total&&o.push(`Commits \xB7 ${r.total}: ${r.keyed} cite their turn, ${r.timing} inside an open turn, ${r.unrooted} unrooted`),o.push(""),o.push("Evidence by provider");for(let[l,i]of Object.entries(e.byProvider).sort((d,m)=>m[1].actions-d[1].actions)){let d=i.rooted||{key:0,turn:0,timing:0},m=i.fidelity==="hook"?`${d.key} by recorded key \xB7 ${d.timing} by timing`:i.turnKeyed?`${d.key} cite the send that opened their turn \xB7 ${d.turn} cite only their transcript turn \xB7 ${d.timing} by timing (no turn key)`:"all by timing (attributed to the latest prompt)";o.push(` ${l.padEnd(10)} ${String(i.actions).padStart(7)} `+(i.fidelity==="hook"?`hook-recorded (turn boundaries known${i.identified?"; calls identified, intent and outcome paired":""})`:i.turnKeyed?"transcript-recorded (agent-written; turns keyed by the CLI's own record)":"transcript only (agent-written; no turn boundaries)")+` \xB7 ${m}`);let h=i.note??tt[l]?.note;h&&o.push(` ${h}`)}o.push("");let a=e.integrity,u=a.chain||{intact:0,intactFromMidLog:0,broken:0,preChain:0};if(o.push(`Log integrity \xB7 ${a.logs} log(s): hash chain intact on ${u.intact} from seq 1 and ${u.intactFromMidLog} from mid-log, broken on ${u.broken}, ${u.preChain} pre-chain (written before the chain); ${a.headMissing} missing their head, ${a.withGaps} with seq gaps, ${a.withRepeats} with repeated seq`),o.push(" (the chain is walked over this node's copy \u2014 advisory; the plane's verified head is the anchor)"),o.push(""),!e.findings.length)o.push("Findings: none.");else{o.push(`Findings \xB7 ${e.findings.length}`);for(let l of e.findings.slice(0,t)){let i=l.to-l.from>B?`${_e(l.from)} \u2192 ${_e(l.to).slice(11)}`:_e(l.from),d=l.actions?`${l.count} prompt(s), ${l.actions} action(s)`:`${l.count}`;o.push(` [${l.severity}] ${l.rule} \xB7 ${l.track||l.trackId}${l.boxId?` \xB7 ${l.boxId}`:""} \xB7 ${i} \xB7 ${d}`),o.push(` ${l.note}`);for(let m of l.samples)o.push(` - ${m}`)}e.findings.length>t&&o.push(` \u2026 ${e.findings.length-t} more (--json for all)`)}return o.push(""),o.push(e.ok?`No action without an origin. ${n||"The log is agent-writable, so this is evidence, not proof."}`:`Actions with NO origin were found \u2014 read the [high] findings above.${n?` ${n}`:""}`),o.join(`
|
|
3
|
+
`)}import X from"node:fs";import at from"node:fs/promises";import re from"node:os";import A from"node:path";import{execFile as ct}from"node:child_process";import{promisify as xn}from"node:util";var Ne=xn(ct),rt="BOSTRAT_BOX_USER",Tn="bostrat-box",lt=Object.freeze(["root","sudo","wheel","admin","docker","lxd","libvirt","disk"]);var An=/^[a-z_][a-z0-9_-]{0,31}$/,In=new Set(["","off","0","false","no","none"]),ut=e=>e.BOSTRAT_STATE_DIR||A.join(e.HOME||re.homedir(),".bostrat");function dt(e){try{return X.readFileSync(e,"utf8").split(`
|
|
4
|
+
`).filter(t=>t&&!t.startsWith("#")).map(t=>t.split(":"))}catch{return[]}}function pt(e,t=process.env){let o=dt(t.BOSTRAT_PASSWD_FILE||"/etc/passwd").find(s=>s[0]===String(e)||s[2]===String(e));return o?{name:o[0],uid:Number(o[2]),gid:Number(o[3]),home:o[5]||null,shell:o[6]||null}:null}function ft(e,t=process.env){let n=dt(t.BOSTRAT_GROUP_FILE||"/etc/group"),o=new Set;for(let s of n)Number(s[2])===e.gid&&o.add(s[0]),(s[3]||"").split(",").includes(e.name)&&o.add(s[0]);return[...o].sort()}var se=()=>typeof process.getuid=="function"?process.getuid():0,Oe=null;function ie(e=process.env){let t=String(e[rt]||"").trim();if(In.has(t.toLowerCase()))return null;let n=`${t}|${e.BOSTRAT_PASSWD_FILE||""}|${e.BOSTRAT_GROUP_FILE||""}|${e.BOSTRAT_BOX_HOME_VIEW||""}`;if(Oe?.key===n)return Oe.value;let o;if(!An.test(t))o={name:t,error:`${rt}="${t}" is not a valid user name`};else{let s=pt(t,e),r=se();if(!s)o={name:t,error:`user "${t}" does not exist \u2014 run scripts/pi/box-user-setup.sh (or the image's useradd)`};else if(s.uid===0)o={name:t,error:`user "${t}" is root`};else if(s.uid===r)o={name:t,error:`user "${t}" is the node's own uid`};else if(!s.home||s.home==="/"||s.home==="/nonexistent")o={name:t,error:`user "${t}" has no home directory`};else{let a=ft(s,e),u=a.filter(l=>lt.includes(l));o=u.length?{name:t,error:`user "${t}" is in ${u.join(", ")} \u2014 a box user must hold no route to root`}:{...s,groups:a,nodeUid:r,rootNode:r===0}}}return Oe={key:n,value:o},o}function On(e=process.env){let t=ie(e);return t&&!t.error?t.home:re.homedir()}function it(e=process.env){let t=ie(e);return!t||t.error||t.rootNode?On(e):e.BOSTRAT_BOX_HOME_VIEW||A.join(ut(e),"box-home")}function Rn(e,t=process.env){let n=ie(t);return!n||n.error?e:n.rootNode?["setpriv",`--reuid=${n.uid}`,`--regid=${n.gid}`,"--init-groups","--","env",`HOME=${n.home}`,`USER=${n.name}`,`LOGNAME=${n.name}`,...e]:["sudo","-n","-H","-u",n.name,"--",...e]}async function Re(e,{env:t=process.env,input:n=null,timeout:o=15e3}={}){let[s,...r]=Rn(e,t);return n==null?Ne(s,r,{timeout:o}):new Promise((a,u)=>{ct(s,r,{timeout:o},(i,d,m)=>i?u(i):a({stdout:d,stderr:m})).stdin.end(n)})}var ve;function vn(e){return e.BOSTRAT_SETFACL_BIN?e.BOSTRAT_SETFACL_BIN:(ve===void 0&&(ve=["/usr/bin/setfacl","/bin/setfacl"].find(t=>X.existsSync(t))||null),ve)}function Nn(e=process.env){let t=re.homedir(),n=ut(e);return[A.join(t,".ssh"),A.join(t,".claude.json"),A.join(t,".claude",".credentials.json"),A.join(t,".codex","auth.json"),A.join(t,".config","gh","hosts.yml"),A.join(t,".cursor","cli-config.json"),A.join(n,"node.env"),A.join(n,"api-token"),A.join(n,"plane-link"),A.join(n,"secure-inputs")]}async function ht({env:e=process.env,probe:t=!0}={}){let n=ie(e),o=await Pn(e);if(!n){let r=pt(se(),e)||{name:re.userInfo().username,uid:se(),gid:-1},a=r.uid===0?["root"]:ft(r,e).filter(i=>lt.includes(i)),u=r.uid!==0&&t?await Dn():!1,l=[...a,...u&&!a.includes("sudo")?["sudo (sudoers)"]:[]];return{level:l.length?"root-capable":"node-uid",user:r.name,routes:l,problems:[],auditd:o}}if(n.error)return{level:"misconfigured",user:n.name,error:n.error,problems:[n.error],auditd:o};let s=[];if(t){try{let{stdout:l}=await Re(["id","-u"],{env:e,timeout:5e3});Number(l.trim())!==n.uid&&s.push(`switching to ${n.name} landed as uid ${l.trim()}`)}catch(l){s.push(`the node cannot switch to ${n.name} (${n.rootNode?"setpriv":"sudo -n -u"} failed: ${jn(l)})`)}if(!s.length){await Re(["sudo","-n","true"],{env:e,timeout:5e3}).then(()=>!0,()=>!1)&&s.push(`${n.name} can sudo`);let l=[];for(let i of Nn(e))X.existsSync(i)&&await Re(["test","-r",i],{env:e,timeout:5e3}).then(()=>!0,()=>!1)&&l.push(i);l.length&&s.push(`${n.name} can read ${l.join(", ")}`)}vn(e)||s.push("setfacl is missing (apt install acl)");let r=it(e);r!==n.home&&!X.existsSync(A.join(r,".claude"))&&s.push(`the read-only mirror of ${n.home} is not mounted at ${r} \u2014 the node cannot read box transcripts`);let a=A.join(n.home,".claude","settings.json"),u=await at.stat(a).catch(()=>null);u&&u.uid===n.uid&&s.push(`${a} is owned by ${n.name} \u2014 the box can rewrite its own hooks`)}return{level:"uid",user:n.name,uid:n.uid,home:n.home,view:it(e),problems:s,auditd:o}}var jn=e=>String(e?.stderr||e?.message||e).trim().split(`
|
|
5
|
+
`)[0].slice(0,160);async function Dn(){try{return await Ne("sudo",["-n","true"],{timeout:3e3}),!0}catch{return!1}}async function Pn(e=process.env,{ask:t=!0}={}){let n=e.BOSTRAT_AUDIT_LOG||"/var/log/audit/audit.log",o=!1;try{await at.access(n,X.constants.R_OK),o=!0}catch{}let s=e.BOSTRAT_AUDIT_RULES_FILE||"/etc/audit/rules.d/bostrat-box.rules",r=X.existsSync(s),a=null;if(t&&r){let u=se()===0?["auditctl",["-l"]]:["sudo",["-n","auditctl","-l"]];try{let{stdout:l}=await Ne(u[0],u[1],{timeout:5e3});a=l.includes(`key=${Tn}`)}catch{a=null}}else r||(a=!1);return{log:n,readable:o,configured:r,rule:a,rulesFile:s}}function mt(e){return e.level==="uid"&&!e.problems.length?`Boxes on this node run as ${e.user}, which holds no route to root and cannot write the event log or its hook settings${e.auditd?.rule===!0&&e.auditd?.readable?"; its every exec is logged by auditd and shipped to the plane":""}.`:e.level==="uid"?`Boxes on this node run as ${e.user}, but containment has gaps (${e.problems.join("; ")}) \u2014 treat this record as evidence, not proof.`:e.level==="root-capable"?`Boxes on this node are root-capable (${e.user}: ${e.routes.join(", ")}), so a root-capable agent can defeat node-side evidence \u2014 this is evidence, not proof.`:e.level==="misconfigured"?`Box containment is configured but unusable (${e.error}); no box can start until it is fixed.`:`Boxes on this node share the node's uid (${e.user}), so an agent can rewrite its own log \u2014 this is evidence, not proof.`}var Mn="BP-AU-R4",xt=160,gt={high:0,medium:1,low:2},yt=200,bt=5,wt=8,St=5,kt=10*6e4,Cn=Object.freeze(["localhost","127.0.0.1","0.0.0.0","github.com","api.github.com","raw.githubusercontent.com","objects.githubusercontent.com","registry.npmjs.org","pypi.org","files.pythonhosted.org"]),Ln=5e3,Pe=new Set(["tool.called","tool.failed"]),je=new Set(["Write","Edit","NotebookEdit"]),Un=e=>String(e??"").replace(/\s+/g," ").trim(),L=(e,t=120)=>Un(e).slice(0,t);function Hn(e,t=xt){let n=String(e??""),o=n.split(/\s*(?:;|&&|\|\||\||\n)\s*/).map(s=>s.trim()).filter(Boolean);return Number.isFinite(t)&&n.length>=t&&o.length&&o.pop(),o}function ae(e){return String(e??"").replace(/\d?>>?\s*\S+|<\s*\S+|2>&1/g," ").trim().split(/\s+/).filter(Boolean)}function Tt(e){let t=0;for(;t<e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[t]);)t++;return{cmd:e[t]??null,rest:e.slice(t+1),at:t}}function Et(e){let t=ae(e),n=t.findIndex(i=>i==="git"||i.endsWith("/git"));if(n<0)return null;for(n++;n<t.length&&t[n].startsWith("-");)(t[n]==="-C"||t[n]==="-c")&&n++,n++;if(t[n]!=="push")return null;let o=[],s=[];for(let i=n+1;i<t.length;i++){let d=t[i];d.startsWith("-")?(o.push(d),(d==="-o"||d==="--push-option"||d==="--repo"||d==="--receive-pack"||d==="--exec")&&i++):s.push(d)}let r=o.some(i=>i==="--force"||/^-[a-zA-Z]+$/.test(i)&&i.includes("f"))||s.some(i=>i.startsWith("+")),a=o.some(i=>i.startsWith("--force-with-lease")||i.startsWith("--force-if-includes")),u=s[0]??null,l=s.slice(1).map(i=>i.replace(/^["']|["']$/g,"").replace(/^\+/,"")).map(i=>i.includes(":")?i.slice(i.indexOf(":")+1):i).filter(i=>i&&!i.startsWith("$"));return{force:r,lease:a,remote:u,dsts:l}}var Me=String.raw`(?:~|\$HOME|\$\{HOME\}|/home/[^\s/"']+|/root|/Users/[^\s/"']+)`,Bn=new RegExp(`${Me}/\\.ssh(?:/|\\b)`),Fn=new RegExp(`${Me}/(?:\\.claude\\.json\\b|\\.claude/\\.credentials\\.json|\\.codex/auth\\.json|\\.cursor(?:/|\\b))`),Wn=/(?:^|[\s/"'=(])(\.env(?:\.[A-Za-z0-9_.-]+)?)(?=$|[\s"'|;&)])/g,qn=/^\.env\.(?:example|sample|template|dist)$/,At=/(\.claude\/settings(?:\.local)?\.json|\.claude\/hooks\/[^\s"']*|\.bostrat\/events(?:\/[^\s"']*)?)/,It=new RegExp(`((?:/etc|/usr/lib|/lib|/run)/systemd/[^\\s"']*|${Me}/\\.config/systemd/[^\\s"']*|/etc/cron(?:tab|\\.d|\\.hourly|\\.daily|\\.weekly|\\.monthly)(?:/[^\\s"']*)?|/var/spool/cron(?:/[^\\s"']*)?)`),Kn=/^(?:enable|disable|start|stop|restart|try-restart|reload|reload-or-restart|daemon-reload|daemon-reexec|link|mask|unmask|edit|preset|preset-all|set-property|revert|isolate|kill|reset-failed|import-environment|set-environment|add-wants|add-requires)$/,Gn=/^\/tmp\/|\/\.bostrat\/tmp\//,Xn=/^(?:refs\/heads\/)?(?:main|master|develop|development|trunk|production|prod|staging|release(?:\/|$)|v\d)/,Vn=/(?:^|\/)track\/|^HEAD$|^refs\/bostrat\//,zn=/https?:\/\/([a-z0-9.-]+\.[a-z]{2,}|localhost|\d+\.\d+\.\d+\.\d+)(?::\d+)?/gi,$t=e=>Bn.test(e)||Fn.test(e),_t=e=>[...String(e??"").matchAll(Wn)].some(t=>!qn.test(t[1])),Yn=e=>At.test(e),Jn=e=>It.test(e),Zn=e=>(e.startsWith("/")||e.startsWith("~"))&&!Gn.test(e);function Ot(e){let{cmd:t,at:n}=Tt(e);if(t==="sudo"){for(n++;n<e.length&&e[n].startsWith("-");)n++;t=e[n]??null}return{cmd:t,at:n}}function Rt(e,t){let n=String(e??"").match(t);if(!n)return null;let o=n[1],s=o.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&");if(new RegExp(`>{1,2}\\s*["']?[^\\s"']*${s}`).test(e))return"redirect";let r=ae(e),{cmd:a}=Ot(r);return!a||!r.some(u=>u.includes(o))?null:/^(?:rm|mv|truncate|chmod|chown|tee|install|shred|unlink|ln|mkdir|touch)$/.test(a)?a:a==="sed"&&r.some(u=>/^-[a-zA-Z]*i/.test(u))?"sed -i":a==="cp"&&r[r.length-1].includes(o)?"cp":/^(?:python3?|perl|node|ruby)$/.test(a)&&/open\([^)]*['"][wa]|writeFile|write_text|appendFile|fs\.write/.test(e)?`${a} write`:null}var Qn=e=>Rt(e,At);function eo(e){let t=ae(e),{cmd:n,at:o}=Ot(t);if(!n)return null;let s=t.slice(o+1);if(n==="crontab"||n.endsWith("/crontab")){let a=!1,u=!1;for(let l=0;l<s.length;l++){if(s[l]==="-u"){l++;continue}s[l]==="-l"||/^-(?:h|V|T|-help|-version)$/.test(s[l])?a=!0:u=!0}return u||!a?"crontab":null}if(n==="systemctl"||n.endsWith("/systemctl")){let a=s.find(u=>!u.startsWith("-"));return a&&Kn.test(a)?`systemctl ${a}`:null}if((n==="loginctl"||n.endsWith("/loginctl"))&&s.includes("enable-linger"))return"loginctl enable-linger";if(n==="at"&&s.length&&/^(?:now|midnight|noon|teatime|today|tomorrow|next|\d|-[fqmvt])/.test(s[0]))return"at";let r=Rt(e,It);return r?`${r} unit/cron path`:null}function to(e){let t=new Set;for(let n of String(e??"").matchAll(zn))t.add(n[1].toLowerCase());return[...t]}function no({knownHosts:e=[]}={}){return{known:new Set([...Cn,...[...e].map(t=>String(t).toLowerCase()).filter(Boolean)]),hosts:new Set,tracks:new Map}}function oo(e,t){let n=e.tracks.get(t??"");return n||e.tracks.set(t??"",n={costs:[],peer:new Map}),n}var I=(e,t,n,o)=>Object.freeze({severity:e,requirement:Mn,note:t,measured:n,detect:o}),Q=(e,t)=>{let n=0,o=null;for(let s of e.calls)if(s.head)for(let r of Hn(s.head,e.headCap)){let a=t(r,s);a&&(n++,o??=L(a))}return n?{count:n,sample:o}:null},De=(e,t)=>{let n=0,o=null;for(let s of e.calls){let r=t(s);r&&(n++,o??=L(r))}return n?{count:n,sample:o}:null},V=Object.freeze({"force-push":I("high","git push --force / -f / +refspec in a command head (--force-with-lease, the Track's own rebase flow, is not one)","2026-10-21 fleet replay \xB7 0 hits / 98,690 hook calls \xB7 1,080 logs 2026-08\u219210 \xB7 58 --force-with-lease pushes excluded by design \u2192 high",(e,t)=>Q(t,n=>{let o=Et(n);return o&&o.force&&!o.lease?n:null})),"protected-config-write":I("high","a write to the box's own harness: .claude/settings*.json, a .claude/hooks/ script or the Track's .bostrat/events/ log \u2014 by a file tool, a redirect, rm/mv/tee/sed -i, or a scripted open-for-write (reads are not hits)","2026-10-21 fleet replay \xB7 0 hits / 98,690 hook calls \xB7 165 reads of those paths (cat, grep, python json.loads) excluded by design \u2192 high",(e,t)=>{let n=0,o=null;for(let r of t.calls)je.has(r.tool)&&r.path&&Yn(r.path)&&(n++,o??=L(`${r.tool} ${r.path}`));let s=Q(t,r=>{let a=Qn(r);return a?`${a}: ${r}`:null});return s&&(n+=s.count,o??=s.sample),n?{count:n,sample:o}:null}),"persistence-install":I("medium","a shell segment that installs something the OS runs on its own \u2014 crontab (install / edit / remove; -l is a read), a mutating systemctl verb (enable / start / restart / daemon-reload \u2026; status / show / cat / list / is-* are reads), loginctl enable-linger, at / batch \u2014 or a write (file tool, redirect, cp / mv / tee / ln / sed -i) into a systemd unit or cron directory: how a process outlives its turn by the OS's hand (BP-AU-D6's other half)","2026-10-21 fleet replay \xB7 11 hits / 99,122 hook calls \xB7 10 Tracks \xB7 0 in the last 14 d \xB7 every hit operator-asked service work on the operator's own Pi (systemctl --user restart/stop/start of bostrat-node, tmp-sweep, a samba disable, one `crontab -` install for a backfill) \xB7 the bare `at` word a quoted `|` regex leaves in a segment (4 hits) excluded by design \u2192 medium (rail), the guard's deny when opted in",(e,t)=>{let n=0,o=null;for(let r of t.calls)je.has(r.tool)&&r.path&&Jn(r.path)&&(n++,o??=L(`${r.tool} ${r.path}`));let s=Q(t,r=>{let a=eo(r);return a?`${a}: ${r}`:null});return s&&(n+=s.count,o??=s.sample),n?{count:n,sample:o}:null}),"push-outside-track":I("medium","git push to a protected branch (main/master/develop/release\u2026), to a branch that is not a Track branch, or to a remote other than origin/upstream \u2014 a Track's own code leaves on its track/ branch or the WIP shadow ref","2026-10-21 fleet replay \xB7 7 hits / 98,690 hook calls \xB7 4 Tracks \xB7 0 in the last 14 d \xB7 every hit operator-instructed work on another repo (two dep/ branches, a fork's multiplayer branch, one HEAD:main) \u2192 medium, not a page",(e,t)=>Q(t,n=>{let o=Et(n);return o&&(o.remote&&!/^(?:origin|upstream)$/.test(o.remote)&&!o.remote.startsWith("$")&&!o.remote.startsWith("-")||o.dsts.some(s=>Xn.test(s)||!Vn.test(s)))?n:null})),"credential-read":I("medium","a command head or file path naming SSH material (~/.ssh) or a vendor auth file (~/.claude.json, ~/.claude/.credentials.json, ~/.codex/auth.json, ~/.cursor) \u2014 the box-auth policy's denylist shapes; ~/.claude/projects (transcripts) is not one","2026-10-21 fleet replay \xB7 12 hits / 98,690 hook calls \xB7 8 Tracks \xB7 3 in the last 14 d \xB7 every hit an operator-asked inspection on the operator's own node (ssh config for a host alias, stat/keys of ~/.claude.json) \u2192 medium until a 14-day window reads 0",(e,t)=>De(t,n=>n.head&&$t(n.head)?n.head:n.path&&$t(n.path)?`${n.tool} ${n.path}`:null)),"dotenv-read":I("low","a command head or file path naming a .env file (.env, .env.local, .env.production \u2026; the committed .env.example / .sample / .template are not ones)","2026-10-21 fleet replay \xB7 309 hits / 98,690 hook calls \xB7 44 Tracks \xB7 0 in the last 14 d \xB7 all of them an instructed `source \u2026/almanac/.env` on the operator's other repos \u2192 low (report only)",(e,t)=>De(t,n=>n.head&&_t(n.head)?n.head:n.path&&_t(n.path)?`${n.tool} ${n.path}`:null)),"privileged-command":I("medium","a shell segment whose command is sudo or docker (after any VAR=value prefix) \u2014 host-level reach from inside a box","2026-10-21 fleet replay \xB7 220 hits (183 events) / 98,690 hook calls \xB7 57 Tracks \xB7 3 in the last 14 d \xB7 sudo = the operator's own journalctl/systemctl asks on the Pi, docker = instructed compose/psql work on the operator's other services \u2192 medium (a hosted node's first hit is the number to watch)",(e,t)=>Q(t,n=>{let{cmd:o}=Tt(ae(n));return o==="sudo"||o==="docker"?n:null})),"write-outside-workspace":I("low","a file tool (Write / Edit / NotebookEdit) on an absolute path \u2014 the hook strips the box's workspace prefix, so an absolute path is outside it \u2014 that is not the box's scratch dir (/tmp/\u2026, \u2026/.bostrat/tmp/\u2026)","2026-10-21 fleet replay \xB7 106 hits / 98,690 hook calls \xB7 10 Tracks \xB7 44 in the last 14 d \xB7 the operator's other repos and Agent-tool worktrees under .bostrat/repos on the operator's own node \u2192 low (report only)",(e,t)=>De(t,n=>je.has(n.tool)&&n.path&&Zn(n.path)?`${n.tool} ${n.path}`:null)),"egress-new-host":I("low","a command head reaching, by URL, a host no box in the fleet has reached before (this projection's lifetime, seeded from the warm tails; KNOWN_HOSTS + PLANE_AUDIT_KNOWN_HOSTS never count) \u2014 a baseline-growth signal, so each host is reported once","2026-10-21 fleet replay \xB7 140 first sightings / 98,690 hook calls \xB7 69 Tracks \xB7 8 in the last 14 d \xB7 198 if the baseline were per Track \xB7 every host a vendor API, a docs site or the operator's own services \u2192 low (report only)",(e,t)=>{let n=0,o=null;for(let s of t.calls)if(s.head)for(let r of to(s.head))t.state.known.has(r)||t.state.hosts.has(r)||(t.state.hosts.add(r),t.state.hosts.size>Ln&&t.state.hosts.delete(t.state.hosts.values().next().value),n++,o??=L(`${r} \xB7 ${s.head}`));return n?{count:n,sample:o}:null}),"spend-spike":I("low",`a cost.recorded turn over ${wt}\xD7 the Track's running median (its last ${yt} cost events, at least ${bt} of them) and over $${St} \u2014 a turn that cost far more than this Track's turns do`,"2026-10-21 fleet replay \xB7 114 hits / 8,421 cost events \xB7 64 Tracks \xB7 2 in the last 14 d \xB7 every hit a long build turn after short prompts (a $25\u201378 turn on a $0.37 median) \u2014 real spend, no one's attack \u2192 low (report only)",(e,t)=>{if(e.type!=="cost.recorded")return null;let n=t.track.costs,o=Number(e.payload?.usd);if(!Number.isFinite(o)||o<0)return null;let s=null;if(n.length>=bt&&o>=St){let r=[...n].sort((u,l)=>u-l),a=r[Math.floor(r.length/2)];o>wt*a&&(s={count:1,sample:`$${o.toFixed(2)} vs median $${a.toFixed(2)} over ${n.length} turns${e.payload?.model?` \xB7 ${e.payload.model}`:""}`})}return n.push(o),n.length>yt&&n.shift(),s}),"peer-injected-call":I("medium",`a tool call within ${kt/6e4} min of a peer.message on the same box with no operator or node-typed turn in between \u2014 the cross-session injection vector (peer-message.js); a peer is never a root (BP-AU-D1)`,"2026-10-21 fleet replay \xB7 0 peer.message events in 288,395 \xB7 the replay cannot bound this rule's rate, so it stays medium (rail) until a window with peer traffic has been measured; the shape itself is BP-AU-D1's stated R4 case",(e,t)=>{if(!t.calls.length)return null;let n=t.track.peer.get(t.boxKey);if(n==null||t.ts-n>kt||t.ts<n)return null;let o=t.calls[0];return{count:t.count,sample:L(`${Math.round((t.ts-n)/1e3)}s after a peer message \xB7 ${o.tool}${o.head?`: ${o.head}`:o.path?` ${o.path}`:""}`)}}),"foreign-session-call":I("medium","a hook-recorded tool call from a claude session that is not the box's own (payload.foreignSession, BP-AU-I12): a nested or stray CLI driving the box's hooks \u2014 R3 never roots it, this names it","2026-10-21 fleet replay \xB7 14 hits / 98,744 hook calls \xB7 3 Tracks \xB7 4 in the last 14 d \xB7 all the xscout loop's nested claude prompts, I12's own acceptance case \u2192 medium (rail), not a page",(e,t)=>{if(!t.calls.length||!e.payload?.foreignSession)return null;let n=t.calls[0];return{count:t.count,sample:L(`session ${String(e.payload.sessionId||"?").slice(0,8)} \xB7 ${n.tool}${n.head?`: ${n.head}`:n.path?` ${n.path}`:""}`)}}),"orphan-process":I("medium","a process the box left running: detached from the pane's tree (its cgroup or pty still lists it), alive at a turn's close and still alive minutes later with no turn open and no background job recorded \u2014 the node's own census (orphan-watch.js, BP-AU-D6), each process once","2026-10-27 fleet replay \xB7 0 audit.procs events in 304,814 (1,121 logs; the type is new, so the replay cannot bound this rule's rate) \xB7 live Pi census the same day: 10 boxes, 2 detached processes, both found by the cgroup census and neither by the pty one (a claude tool call runs on pipes) \u2014 one planted `nohup sleep &`, one real `flyctl agent run` that a lead's flyctl call had left behind 10 h earlier \u2192 medium (rail), never a page, until a window of live samples has been measured",e=>{if(e.type!=="audit.procs"||e.source!=="proc")return null;let t=e.payload||{},n=Array.isArray(t.orphans)?t.orphans:[],o=n.length+(Number(t.dropped)||0);if(!o)return null;let s=n[0]||{},r=Number.isFinite(s.sinceMs)?` \xB7 alive ${Math.max(1,Math.round(s.sinceMs/6e4))} min`:"";return{count:o,sample:L(`${s.comm||"process"}${s.cmd?`: ${s.cmd}`:""}${s.via?` [${s.via}]`:""}${r}${o>1?` (+${o-1} more)`:""}`)}}),"unknown-harness-shape":I("medium","a harness-made user turn of a shape chat-notes.js does not list (harness.shape.seen with known: false, harness-shapes.js): Claude Code wrote a new kind of its own turn into a transcript \u2014 hidden from chat by the generic catch, counted once per shape per node here; the node pages the operator once on first sight, this row is the fleet's record","2026-10-28 fleet replay \xB7 0 harness.shape.seen events in 311,055 (1,132 logs; the type is new, so the replay cannot bound this rule's rate) \xB7 transcript scan the same day: 2,863 claude user turns in 1,539 transcripts over 90 d, 144 of them harness-made (isMeta), 9 distinct shapes, 4 of which had rendered in chat as operator messages (<system-reminder><tools-used-before-summary> on 2 Tracks under 2.1.312, a usage-limit-reset notice, a safety-classifier notice) \u2192 medium (rail finding + security row), never a page from the plane: the once-per-shape page is the node's, so a release that adds one shape pages once, not per Track per hour",e=>{if(e.type!=="harness.shape.seen"||e.source!=="transcript")return null;let t=e.payload||{};if(t.known!==!1||!t.key)return null;let n=`${t.provider||"cli"}${t.cliVersion?` ${t.cliVersion}`:""}`;return{count:1,sample:L(`${t.key} \xB7 ${n}${t.head?` \xB7 ${t.head}`:""}`)}})}),z=Object.freeze(Object.keys(V)),so=new Set([...Pe,"cost.recorded","peer.message","turn.user","audit.procs","harness.shape.seen"]);function ro(e,t,{rules:n=null,headCap:o=xt}={}){if(!t||!so.has(t.type)||t.source==="plane")return[];let s=oo(e,t.trackId),r=t.payload||{},a=t.boxId??"",u=Number(t.ts)||0;if(t.type==="turn.user")return(t.actor==="user"||t.actor==="orchestrator")&&s.peer.delete(a),[];if(t.type==="peer.message")return s.peer.set(a,u),[];let l=Pe.has(t.type)&&t.source==="hook",d={calls:l?P(r):[],count:l?q(r):0,track:s,state:e,ts:u,boxKey:a,headCap:o},m=[];for(let h of n||z){let g=V[h];if(!g)continue;let S=null;try{S=g.detect(t,d)}catch{S=null}S&&S.count>0&&m.push({rule:h,count:S.count,sample:S.sample??null})}return m}function vt(e,{days:t=14,now:n=Date.now(),rules:o=null}={}){let s=t?n-t*864e5:0,r=no(),a=o||z,u={days:t||null,logs:0,events:0,calls:0,callsInWindow:0,rules:Object.fromEntries(a.map(i=>[i,{severity:V[i].severity,note:V[i].note,measured:V[i].measured,hits:0,hitsInWindow:0,events:0,tracks:0,tracksInWindow:0,perTenK:0,samples:[]}]))},l=Object.fromEntries(a.map(i=>[i,{all:new Set,win:new Set}]));for(let i of e||[]){let d=Array.isArray(i?.events)?[...i.events].sort((m,h)=>(Number(m.ts)||0)-(Number(h.ts)||0)):[];if(d.length){u.logs++;for(let m of d){u.events++;let h=(Number(m.ts)||0)>=s;if(Pe.has(m.type)&&m.source==="hook"){let g=q(m.payload||{});u.calls+=g,h&&(u.callsInWindow+=g)}for(let g of ro(r,m,{rules:a})){let S=u.rules[g.rule];if(S.hits+=g.count,S.events++,l[g.rule].all.add(m.trackId??i.trackId),h&&(S.hitsInWindow+=g.count,l[g.rule].win.add(m.trackId??i.trackId)),g.sample){let j=S.samples.find(b=>b.text===g.sample);j?j.n++:S.samples.length<200&&S.samples.push({text:g.sample,n:1,ts:Number(m.ts)||0,trackId:m.trackId??i.trackId,name:i.name??null})}}}}}for(let i of a){let d=u.rules[i];d.tracks=l[i].all.size,d.tracksInWindow=l[i].win.size,d.perTenK=u.calls?Number((d.hits/u.calls*1e4).toFixed(2)):0,d.samples.sort((m,h)=>h.n-m.n||h.ts-m.ts),d.samples=d.samples.slice(0,50)}return u.ok=!a.some(i=>V[i].severity==="high"&&u.rules[i].hitsInWindow>0),u}var io=e=>new Date(e).toISOString().slice(0,10);function Nt(e,{samples:t=5}={}){let n=[];n.push(`R4 DETECTOR REPLAY \xB7 ${e.days?`window last ${e.days}d`:"all history"} \xB7 ${e.logs} logs \xB7 ${e.events.toLocaleString()} events \xB7 ${e.calls.toLocaleString()} hook calls (${e.callsInWindow.toLocaleString()} in window)`),n.push("A rule's severity is what its measured rate supports; `high` pages (D7). Hits are what the plane fold would have flagged.");let o=Object.keys(e.rules).sort((s,r)=>gt[e.rules[s].severity]-gt[e.rules[r].severity]||s.localeCompare(r));for(let s of o){let r=e.rules[s];n.push(""),n.push(`${s} [${r.severity}] \xB7 ${r.hits} hit(s) all-time (${r.events} events, ${r.tracks} Tracks, ${r.perTenK}/10k calls) \xB7 ${r.hitsInWindow} in window (${r.tracksInWindow} Tracks)`),n.push(` ${r.note}`),r.measured&&n.push(` measured: ${r.measured}`);for(let a of r.samples.slice(0,t))n.push(` ${a.n}\xD7 ${io(a.ts)} ${a.name?`${a.name} `:""}(${a.trackId}) ${a.text}`)}return n.push(""),n.push(e.ok?"ok: no high-severity detector hit in the window":"ATTENTION: a high-severity detector hit inside the window (exit 2)"),n.join(`
|
|
6
|
+
`)}import Ft from"node:fs/promises";import Ro from"node:os";import qe from"node:path";import He from"node:fs/promises";import Y from"node:path";import go from"node:os";import{EventEmitter as yo}from"node:events";import{randomUUID as bo}from"node:crypto";import ce from"node:fs";import ue from"node:path";import ao from"node:os";var co=()=>process.env.BOSTRAT_STATE_DIR||ue.join(process.env.HOME||ao.tmpdir(),".bostrat"),lo="secure-inputs",Pt=()=>ue.join(co(),lo),uo=/^[A-Z][A-Z0-9_]{0,63}$/,po=/^[A-Za-z0-9_-]{1,80}$/,fo=16*1024;var Ce=8;function ho(e){return typeof e!="string"||!e?"a value is required":e.length<Ce?`a value must be at least ${Ce} characters`:Buffer.byteLength(e,"utf8")>fo?"that value is too large (16 KiB max)":/[\0\r\n]/.test(e)?"a value must be one line":null}var Le=new Map,ee=[],jt=!1;function mo(){let e=[];for(let t of Le.values())for(let[n,{value:o}]of t){let s=`\u2039${n}\u203A`,r=new Set([o]);r.add(encodeURIComponent(o)),r.add(Buffer.from(o,"utf8").toString("base64")),r.add(Buffer.from(o,"utf8").toString("base64url")),r.add(JSON.stringify(o).slice(1,-1));for(let a of r)a&&a.length>=Ce&&e.push({needle:a,mark:s})}e.sort((t,n)=>n.needle.length-t.needle.length),ee=e}function Dt(e){return ue.join(Pt(),e)}function Mt(){if(jt)return;jt=!0,Le.clear();let e=[];try{e=ce.readdirSync(Pt(),{withFileTypes:!0})}catch{e=[]}for(let t of e){if(!t.isDirectory()||!po.test(t.name))continue;let n=new Map,o=[];try{o=ce.readdirSync(Dt(t.name),{withFileTypes:!0})}catch{continue}for(let s of o)if(!(!s.isFile()||!uo.test(s.name)))try{let r=ue.join(Dt(t.name),s.name),a=ce.readFileSync(r,"utf8");if(ho(a))continue;n.set(s.name,{value:a,addedAt:ce.statSync(r).mtimeMs})}catch{}n.size&&Le.set(t.name,n)}mo()}function Ct(){return Mt(),ee.length>0}function Ue(e){if(typeof e!="string"||!e||(Mt(),!ee.length))return e;let t=e;for(let{needle:n,mark:o}of ee)t.includes(n)&&(t=t.split(n).join(o));return t}function le(e,t=new WeakSet){if(typeof e=="string")return Ue(e);if(!e||typeof e!="object"||!ee.length||t.has(e))return e;if(t.add(e),Array.isArray(e)){let s=!1,r=e.map(a=>{let u=le(a,t);return u!==a&&(s=!0),u});return s?r:e}if(Buffer.isBuffer(e)||e instanceof Date)return e;let n=!1,o={};for(let s of Object.keys(e)){let r=le(e[s],t);r!==e[s]&&(n=!0),o[s]=r}return n?o:e}var wo=new Set(["session.ready","session.foreign","turn.user","turn.agent","turn.retracted","peer.message","provider.error","delivery.queued","delivery.settled","turn.steered","turn.interrupted","turn.aborted","tool.started","tool.called","tool.failed","tool.result","prompt.raised","prompt.answered","prompt.dismissed","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","box.stuck.detected","box.stuck.answered","context.usage","context.refresh.offered","context.refresh.answered","handoff.offered","handoff.answered","automation.fired","refresh.flow.changed","cost.recorded","memory.seed","memory.harvested","handoff.artifact","code.pushed","code.committed","code.rework","rework.cause.offered","rework.cause.answered","rework.cause.retracted","handoff.started","handoff.step","handoff.done","handoff.failed","node.saturated","node.restarted","subtask.delegated","subtask.merged","job.started","job.stalled","job.stopped","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","pr.update.reported","pr.train.boarded","pr.train.landed","pr.train.ejected","pr.train.rebuilt","box.state.changed","view.state.changed","view.spinup.failed","deps.prepare","deps.setup","secret.added","secret.renamed","secret.removed","secret.used","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","replay.spec.offered","replay.spec.answered","replay.run.started","replay.run.nudged","replay.run.capped","replay.run.stopped","replay.run.checked","replay.run.finished","followup.armed","followup.armed.cancelled","followup.arm.saved","followup.offered","followup.answered","followup.create.offered","followup.create.answered","followup.created","planning.change.offered","planning.change.answered","planning.changed","notify.raised","usage.cap.hit","provider.limit.hit","usage.cap.warning","provider.login.started","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.phase.changed","track.operation.changed","track.obligations.changed","track.interrupted.changed","track.circuit.tagged","blueprint.item.launched","blueprint.item.bound","blueprint.status.offered","blueprint.merge.unlocked","blueprint.gate.met","decision.recorded","estimate.recorded","estimate.closed","changelog.published","track.conflict.predicted","track.conflict.cleared","audit.chain.broken","audit.chain.healed","audit.flagged","audit.exec","audit.procs","harness.shape.seen","stall.detected","error"]),So=new Set(["user","agent","orchestrator","system","peer"]),ko=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli","auditd","proc","plane"]),Be=new yo;Be.setMaxListeners(0);var Fe=process.env.BOSTRAT_STATE_DIR||Y.join(process.env.HOME||go.tmpdir(),".bostrat");function Lt(e){return e?.cwd?Y.join(e.cwd,".bostrat","events"):Y.join(Fe,"events")}var Ut=(e,t)=>Y.join(e,`${t}.ndjson`),te=new Map,pe=new Map,de=new Map;async function Eo(e,t){if(te.has(t))return;let n=await To(e,t),o=0,s=null;for(let r of n)r.seq>o&&(o=r.seq),Ee(r)&&(s=r);te.set(t,o),pe.set(t,s?oe(s):null)}function $o(e,t){let o=(de.get(e)??Promise.resolve()).then(t,t),s=o.then(()=>{},()=>{});return de.set(e,s),s.then(()=>{de.get(e)===s&&de.delete(e)}),o}function _o(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 o=n.length-1;o>=0;o--){let s=n[o].startedAt;if(s==null||s<=t)return o}return 0}}async function Ht(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!wo.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!So.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!ko.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let o=typeof e=="string"?Y.join(Fe,"events"):Lt(e),s=t.epoch;if(s==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let r=e.boxes.find(a=>a.name===t.boxId);s=_o(r,Number(t.payload?.ts??t.ts)||Date.now())}return $o(n,async()=>{await Eo(o,n);let r=te.get(n)+1;te.set(n,r);let a=pe.get(n)??null,u={id:t.id||bo(),trackId:n,boxId:t.boxId??null,...s!=null?{epoch:s}:{},seq:r,prev:a,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}:{}};if(Ct()){let d=le(u);d!==u&&Object.assign(u,d)}let l=JSON.stringify(u),i=u;fe(l)&&(l=he(l),i=JSON.parse(l)),pe.set(n,oe(i));try{await He.mkdir(o,{recursive:!0}),await He.appendFile(Ut(o,n),l+`
|
|
7
|
+
`)}catch(d){throw te.set(n,r-1),pe.set(n,a),d}return Be.emit("event",i),Be.emit(`track:${n}`,i),i})}var xo=/(?<!\\)((?:\\\\)*)\\u0000/g,fe=e=>e.includes("\\u0000"),he=e=>e.replace(xo,"$1\\ufffd");async function To(e,t){let n,o;typeof e=="string"&&t?(n=e,o=t):(o=typeof e=="string"?e:e?.id,n=typeof e=="string"?Y.join(Fe,"events"):Lt(e));let s;try{s=await He.readFile(Ut(n,o),"utf8")}catch{return[]}return Ao(s)}function Ao(e){let t=[];for(let n of e.split(`
|
|
8
|
+
`))if(n.trim())try{t.push(JSON.parse(fe(n)?he(n):n))}catch{}return t}import me from"node:fs/promises";import Io from"node:path";var Oo=0;async function Bt(e,t,{mode:n}={}){await me.mkdir(Io.dirname(e),{recursive:!0});let o=`${e}.${process.pid}.${++Oo}.tmp`;try{await me.writeFile(o,JSON.stringify(t,null,2)+`
|
|
9
|
+
`,n!=null?{mode:n}:void 0),await me.rename(o,e)}catch(s){throw await me.unlink(o).catch(()=>{}),s}}var vo=()=>process.env.BOSTRAT_STATE_DIR||qe.join(process.env.HOME||Ro.tmpdir(),".bostrat"),No="harness-shapes.json",jo=12,Do=8,Po=80,Mo=e=>Ue(String(e||"").replace(/\s+/g," ").trim().slice(0,Po));function Co({file:e=null,deps:t={},log:n=console}={}){let o={appendEvent:Ht,now:()=>Date.now(),...t},s=()=>e||qe.join(vo(),No),r=null,a=null,u=Promise.resolve();async function l(){return r||(a||(a=Ft.readFile(s(),"utf8").then(h=>{let g=JSON.parse(h);return g&&typeof g.shapes=="object"&&g.shapes?{version:1,shapes:g.shapes}:{version:1,shapes:{}}}).catch(()=>({version:1,shapes:{}})).then(h=>r=h)),a)}function i(){let h=JSON.stringify(r);return u=u.then(async()=>{await Ft.mkdir(qe.dirname(s()),{recursive:!0}),await Bt(s(),JSON.parse(h))}).catch(g=>n.warn?.(`[harness-shapes] write failed: ${g?.message||g}`)),u}async function d(h,g,S){if(!S||S.kind!=="harness"||!S.key)return null;let j=await l(),b=o.now(),x=String(S.key).slice(0,200),T=g?.agent||null,v=typeof g?.cliVersion=="string"?g.cliVersion:null,k=j.shapes[x],D=!k;D&&(k=j.shapes[x]={key:x,tags:Array.isArray(S.tags)?S.tags.slice(0,20):[],known:!!S.known,head:Mo(S.head||g?.text),firstSeen:b,lastSeen:b,count:0,tracks:[],providers:{},versions:[],paged:!1}),k.lastSeen=b,k.count++,S.known&&!k.known&&(k.known=!0),h?.id&&!k.tracks.includes(h.id)&&(k.tracks.push(h.id),k.tracks.length>jo&&k.tracks.shift()),T&&(k.providers[T]=(k.providers[T]||0)+1),v&&!k.versions.includes(v)&&(k.versions.push(v),k.versions.length>Do&&k.versions.shift());let O=!1;if(D&&h?.id){try{await o.appendEvent(h,{type:"harness.shape.seen",actor:"system",source:"transcript",boxId:g?.box??null,provider:T,payload:{key:x,tags:k.tags,known:k.known,head:k.head,provider:T,cliVersion:v,box:g?.box??null,count:1,ts:b}})}catch(F){n.warn?.(`[harness-shapes] harness.shape.seen append failed (${h.id}): ${F?.message||F}`)}if(!k.known&&!k.paged){k.paged=!0,O=!0;let F=k.tags.length?`<${k.tags.join("> <")}>`:`"${k.head}"`;try{await o.appendEvent(h,{type:"notify.raised",actor:"system",source:"transcript",boxId:g?.box??null,provider:T,payload:{title:"New Claude Code message shape hidden",body:`${T||"The CLI"}${v?` ${v}`:""} wrote a harness turn Bostrat had not seen before \u2014 ${F} \u2014 into ${h.name||h.id}. It was hidden from chat and recorded; run bostrat-audit --harness to review, and add the tag to HARNESS_TAGS once it is understood.`,urgency:"normal",trackName:h.name||null,ts:b}})}catch(c){n.warn?.(`[harness-shapes] notify.raised append failed (${h.id}): ${c?.message||c}`)}}n.log?.(`[harness-shapes] new shape ${x}${k.known?"":" (NOT in HARNESS_TAGS \u2014 paged)"} on ${h.id}${v?` \xB7 ${T} ${v}`:""}`)}return await i(),{first:D,paged:O,record:k}}async function m(){let h=await l();return Object.values(h.shapes).sort((g,S)=>S.lastSeen-g.lastSeen)}return{observe:d,list:m,flush:()=>u,reset(){r=null,a=null}}}var We=null;function Lo(){return We||(We=Co()),We}var qt=()=>Lo().list();var Wt=e=>Number.isFinite(e)?new Date(e).toISOString().slice(0,16).replace("T"," "):"?";function Kt(e,{knownTags:t=[]}={}){let n=[],o=Array.isArray(e)?e:[],s=o.filter(r=>!r.known);n.push(`HARNESS SHAPES \xB7 ${o.length} shape(s) seen on this node \xB7 ${s.length} not in HARNESS_TAGS`),n.push("A harness-made user turn (Claude Code's own isMeta / tag-block turns) is hidden from chat and counted here by shape; a shape outside the table paged once when first seen."),o.length||n.push("(none yet \u2014 the registry fills as Tracks ingest harness-made turns)");for(let r of o){let a=r.versions?.length?` \xB7 ${Object.keys(r.providers||{}).join("/")||"cli"} ${r.versions.join(", ")}`:"";n.push(""),n.push(`${r.key} [${r.known?"known":"NEW"}] \xB7 ${r.count}\xD7 \xB7 ${r.tracks?.length||0} Track(s) \xB7 first ${Wt(r.firstSeen)} \xB7 last ${Wt(r.lastSeen)}${a}${r.paged?" \xB7 paged":""}`),r.head&&n.push(` ${r.head}`)}return t.length&&(n.push(""),n.push(`table (chat-notes.js HARNESS_TAGS): ${t.join(", ")}`)),n.join(`
|
|
10
|
+
`)}var Uo=["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"],Is=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Uo.join("|")})[:*\t ]*$`,"gim");var Rs=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),vs=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})();var Ns=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})();var ge=Object.freeze(["system-reminder","tools-used-before-summary","task-notification","command-name","command-message","command-args","local-command-stdout","local-command-stderr","local-command-caveat","user-prompt-submit-hook","ide_selection","ide_opened_file","ide_diagnostics"]),Fs=new Set(ge);var be=process.env.BOSTRAT_STATE_DIR||K.join(process.env.HOME||Bo.tmpdir(),".bostrat"),R=process.argv.slice(2),we=e=>{let t=R.indexOf(e);return t>=0?R[t+1]:null};(R.includes("--help")||R.includes("-h"))&&(console.log("usage: bostrat-audit [--days N | --all] [--track <id-or-name>] [--limit N] [--json]"),console.log(" bostrat-audit --detectors [--days N | --all] [--track <id-or-name>] [--rule <name>]... [--limit N] [--json]"),console.log(` rules: ${z.join(", ")}`),console.log(" bostrat-audit --harness [--json] # the harness-made user-turn shapes this node has hidden from chat, by shape"),process.exit(0));if(R.includes("--harness")){let e=await qt();console.log(R.includes("--json")?JSON.stringify({shapes:e,knownTags:ge},null,2):Kt(e,{knownTags:ge})),process.exit(0)}var Xe=[];try{Xe=JSON.parse(await ye.readFile(K.join(be,"tracks.json"),"utf8")).tracks||[]}catch{}var Fo=new Map(Xe.map(e=>[e.id,e.name])),Ve=new Set([K.join(be,"events")]);for(let e of Xe)e.cwd&&Ve.add(K.join(e.cwd,".bostrat","events"));try{for(let e of await ye.readdir(K.join(be,"workspaces")))Ve.add(K.join(be,"workspaces",e,".bostrat","events"))}catch{}var Ke=we("--track"),ze=[];for(let e of Ve){let t=[];try{t=(await ye.readdir(e)).filter(n=>n.endsWith(".ndjson"))}catch{continue}for(let n of t){let o=n.slice(0,-7),s=Fo.get(o)||null;if(Ke&&o!==Ke&&!(s||"").toLowerCase().includes(Ke.toLowerCase()))continue;let r=[];for(let a of(await ye.readFile(K.join(e,n),"utf8")).split(`
|
|
11
11
|
`))if(a.trim())try{r.push(JSON.parse(fe(a)?he(a):a))}catch{}r.length&&ze.push({trackId:o,name:s,events:r})}}var Gt=R.includes("--all")?0:Number(we("--days"))||14;if(R.includes("--detectors")){let e=R.flatMap((o,s)=>o==="--rule"&&R[s+1]?[R[s+1]]:[]),t=e.filter(o=>!z.includes(o));t.length&&(console.error(`unknown rule(s): ${t.join(", ")} \u2014 known: ${z.join(", ")}`),process.exit(1));let n=vt(ze,{days:Gt,rules:e.length?e:null});console.log(R.includes("--json")?JSON.stringify(n,null,2):Nt(n,{samples:Number(we("--limit"))||5})),process.exit(n.ok?0:2)}var ne=ot(ze,{days:Gt}),Ge=await ht().catch(()=>null);Ge&&(ne.containment={...Ge,sentence:mt(Ge)});console.log(R.includes("--json")?JSON.stringify(ne,null,2):st(ne,{limit:Number(we("--limit"))||20,containment:ne.containment?.sentence}));process.exit(ne.ok?0:2);
|
package/bin/bostrat-blueprint
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
2
|
+
var y=process.env.BOSTRAT_AGENT_URL||`http://127.0.0.1:${process.env.BOSTRAT_AGENT_PORT||3362}`;async function f(e,a,o){let i=await fetch(`${y}${a}`,{method:e,headers:o?{"content-type":"application/json"}:void 0,body:o?JSON.stringify(o):void 0}),s=await i.json().catch(()=>({}));if(!i.ok)throw Object.assign(new Error(s.error||`HTTP ${i.status}`),{status:i.status,data:s});return s}function u(e){console.error(`[bostrat-blueprint] ${e}`),process.exit(1)}function v(e,a){if(console.error(`[bostrat-blueprint] refused: ${e.error}`),e.code==="stale-base"){if(console.error(` plane holds v${e.current}; this copy was built on v${e.yourBase}.`),e.drift?.length){console.error(" item-level difference:");for(let o of e.drift)console.error(` ${String(o.itemId).padEnd(12)} plane: ${String(o.plane).padEnd(7)} file: ${o.file}`)}else console.error(" no item differs \u2014 the newer version changed prose only.");console.error(" Bring this copy up to date, then republish:"),console.error(" git fetch origin && git merge origin/main # siblings' ticks land on main with their PRs"),console.error(" (copy any ticked item you still lack VERBATIM \u2014 never invent a sibling's note)"),console.error(` bostrat-blueprint show && bostrat-blueprint update ${a}`),console.error(` Or, once the file carries everything above: bostrat-blueprint update --base ${e.current} ${a}`)}else e.code==="untick"&&(console.error(` would un-tick: ${e.untick.join(", ")} (plane v${e.current} has them [x]).`),console.error(" Copy their ticks + status notes into the file (merge origin/main first), then republish."));console.error(" --force publishes the file exactly as it is (un-ticking the items above). Only when that is the intent."),process.exit(1)}function w(e){if(e){console.log(` parsed: ${e.sections.recognized} recognized section(s), ${e.items.total} item(s) (${e.items.bindable} bindable)`);for(let a of e.issues??[])console.log(` \u26A0 ${a.message}`)}}var r=process.argv.slice(2),g=r.shift();if(g==="add"||g==="update"){let e=null,a=null,o=null,i=null,s=null,l=!1;for(let n=0;n<r.length;n++)r[n]==="--circuit"?e=r[++n]:r[n]==="--new-circuit"?a=r[++n]:r[n]==="--name"?o=r[++n]:r[n]==="--base"?s=r[++n]:r[n]==="--force"?l=!0:i=r[n];i||u(`usage: bostrat-blueprint ${g} [--circuit <name-or-id>] [--new-circuit "<name>"] [--name "<title>"] [--base <v>] [--force] <file.md>`),s!=null&&!/^[1-9]\d*$/.test(String(s))&&u(`--base takes a version number (what \`show\` prints), got "${s}"`);try{let n=await(await import("node:fs/promises")).readFile(i,"utf8"),h=null,m=null;try{let{execFileSync:t}=await import("node:child_process"),p=await import("node:path"),c=p.resolve(i),b=t("git",["rev-parse","--show-toplevel"],{cwd:p.dirname(c),encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim(),$=p.relative(b,c);$&&!$.startsWith("..")&&(h=$.split(p.sep).join("/"),m=b)}catch{}let d=await f("POST","/api/blueprints/publish",{cwd:process.cwd(),circuit:e,newCircuit:a,name:o,markdown:n,base:s==null?null:Number(s),force:l,path:h,root:m});console.log(`[bostrat-blueprint] published \u201C${d.blueprint.name}\u201D v${d.blueprint.version} on circuit \u201C${d.circuit.name}\u201D${d.base?` (on v${d.base})`:""}${l?" (forced)":""}`),w(d.report),d.linked&&console.log(` This Track is now in circuit \u201C${d.circuit.name}\u201D (it was in none) \u2014 the blueprint's item ids in a closing message are launch controls from here.`),d.blueprint.version===1&&console.log(" The circuit page now carries this blueprint; PRs/commits mentioning BP ids show as evidence \u2014 tick [x] + a *(done \u2026)* note in the same PR as the item's work.")}catch(n){n.status===409&&n.data?.code&&v(n.data,i),u(n.message)}}else if(g==="show"||g==="report")try{let e=await f("GET",`/api/blueprints/show?cwd=${encodeURIComponent(process.cwd())}${r[0]?`&circuit=${encodeURIComponent(r[0])}`:""}`);if(console.log(`[bostrat-blueprint] \u201C${e.blueprint.name}\u201D v${e.blueprint.version} (circuit ${e.blueprint.circuitId})`),w(e.report),g==="show"){let a=!!e.graph?.edges,o=e.readyAt||"merged",i=new Set,s=t=>{let p=Array.isArray(t.waits)&&t.waits.length?t.waits:null;return p?p.map(c=>c.gate&&c.gate!==o?(i.add(c.gate),`${c.itemId} ${c.gate}`):c.itemId).join(", "):(t.waitsOn??[]).join(", ")},l=t=>t.bindable?t.done?"done":a?t.ready?`ready${t.seam?" \xB7 seam":""}`:`waits on ${s(t)}`:"":"",n=e.items.some(t=>t.bindable&&t.stageLine),h=e.items.map(t=>({i:t,id:t.bindable?t.itemId:"(no id)",pose:l(t)})),m=Math.max(8,...h.map(t=>t.id.length)),d=n?Math.max(0,...h.map(t=>t.pose?t.pose.length+2:0)):0;for(let{i:t,id:p,pose:c}of h){if(!n){console.log(` ${p.padEnd(8)} ${t.sectionKind.padEnd(14)} ${c?`[${c}] `:""}${t.text}`);continue}let b=t.bindable&&t.stageLine?t.stageLine:"";console.log(` ${p.padEnd(m)} ${t.sectionKind.padEnd(14)} ${(c?`[${c}]`:"").padEnd(d)}${d?" ":""}${b}`.trimEnd()),t.text&&console.log(` ${"".padEnd(m)} ${t.text}`)}n&&console.log(" (stage = what the plane observed, with its evidence. merged or higher is a fact \u2014 do not re-verify it; copy it as printed)"),a&&(console.log(e.readyAt?` (readiness = every \`after\` item is at ${e.readyAt} or higher \u2014 this blueprint's ready-at; a What-remains list bolds only ready items)`:" (readiness = every `after` item presents as done; a What-remains list bolds only ready items)"),i.size&&console.log(" (gate words: merged = the dependency's code is on main; released = it is in a cut release; live = it is running on the repo's deploy surfaces. A word beside an id is that edge's own gate; an edge with no word waits at ready-at)"))}}catch(e){u(e.message)}else if(g==="remains"){let e='usage: bostrat-blueprint remains [--brief <BP-id>="<one sentence>"]\u2026',a={};for(let o=0;o<r.length;o++){r[o]!=="--brief"&&u(`unknown argument "${r[o]}" \u2014 ${e}`);let i=r[++o];i==null&&u(e);let s=i.indexOf("="),l=(s>0?i.slice(0,s):i).trim(),n=s>0?i.slice(s+1):r[++o];(!/^BP-[A-Z0-9]+(?:-[A-Z0-9]+)*$/i.test(l)||!String(n??"").trim())&&u(`--brief takes <BP-id>="<one sentence>", got "${i}" \u2014 ${e}`),a[l.toUpperCase()]=String(n).trim()}try{let o=await f("POST","/api/blueprints/remains",{cwd:process.cwd(),briefs:a}),i=o.counts;console.log(`[bostrat-blueprint] status card posted for \u201C${o.name??"blueprint"}\u201D v${o.version} \u2014 ${i.done} of ${i.total} merged \xB7 ${i.ready} ready \xB7 ${i.waiting} waiting`),o.mine?.length&&console.log(` this Track: ${o.mine.map(s=>`${s.itemId} (${s.stageLine??s.stage??"open"})`).join(", ")}`);for(let s of o.ready??[])console.log(` ready ${s.itemId} ${s.text}${o.briefs?.[s.itemId]?` \u2014 brief: ${o.briefs[s.itemId]}`:""}`);for(let s of o.waiting??[])console.log(` waiting ${s.itemId} ${s.text} \u2014 waits on ${(s.waits??[]).map(l=>`${l.itemId}${l.gate?` ${l.gate}`:""}`).join(", ")||"(unmet)"}`);!i.ready&&!i.waiting&&console.log(" nothing open \u2014 every item presents done."),console.log(" The card carries these rows under your closing message. Do not repeat them in your message \u2014 close by saying what you did and end your turn.")}catch(o){console.error(`[bostrat-blueprint] no status card posted: ${o.message}`),console.error(' Fall back to prose: end your message with a short "What remains" list \u2014 "Ready now" items with the FULL id in bold first (**BP-X-I3**: what it still needs), then "Waiting" items (id not bold) with what each waits on.'),process.exit(1)}}else if(g==="tag"){r.length||u("usage: bostrat-blueprint tag <BP-id> [<BP-id>\u2026]");try{let e=await f("POST","/api/blueprints/tag",{cwd:process.cwd(),itemIds:r});console.log(`[bostrat-blueprint] tagged this Track onto ${e.itemIds.join(", ")}`),console.log(" (association + cost attribution \u2014 completion still comes from merged PRs or a manual tick)")}catch(e){u(e.message)}}else if(g==="hubs")try{let e=await f("GET",`/api/blueprints/hubs?cwd=${encodeURIComponent(process.cwd())}${r[0]?`&repo=${encodeURIComponent(r[0])}`:""}`);if(!e.hubs?.length)console.log(`[bostrat-blueprint] ${e.repo}: no hub files yet (${e.tracks??0} Track(s) with observed edits)`);else{console.log(`[bostrat-blueprint] ${e.repo}: hub files \u2014 share of ${e.tracks} Track(s) with observed edits that wrote each`);for(let a of e.hubs)console.log(` ${String(Math.round(a.share*100)).padStart(3)}% ${a.path}`);console.log(" (route edits to these through the seam item, or order the items that touch them with `after`)")}}catch(e){u(e.message)}else u("usage: bostrat-blueprint <add|update|show|report|tag|hubs|remains> \u2026 (add [--circuit <ref>] <file.md>)");
|
package/bin/bostrat-replay
CHANGED
|
@@ -11,7 +11,7 @@ ${d}`);let s=await N(e),t;try{t=await h("POST","/api/replay/specs",{cwd:process.
|
|
|
11
11
|
${d}`);let s=await $(`/api/replay/specs/${encodeURIComponent(o)}`).catch(u=>r(u.message)),t=await N(e),a={cwd:process.cwd(),statement:t,note:e.note??null};e.suite!==void 0&&(a.suite=e.suite);let n;try{n=await h("POST",`/api/replay/specs/${encodeURIComponent(s.spec.id)}/revise`,a)}catch(u){r(u.message)}let c=n.spec;k(c),console.log(` v${c.version} drafted \u2014 a fresh approval card is in this Track's chat${c.approvedVersion!=null?`; v${c.approvedVersion} stays the bench entry until this one is approved`:""}.`),console.log(" This is a BLOCKING prompt: stop and end your turn.")}async function B(e){let{specs:o}=await $(`/api/replay/specs${e.status?`?status=${encodeURIComponent(e.status)}`:""}`);if(e.json){console.log(JSON.stringify(o,null,2));return}if(!o.length){console.log("[bostrat-replay] no task specs on this node yet \u2014 `bostrat-replay census --rows` lists the eligible sources");return}let s={drafted:0,approved:0,dismissed:0};for(let t of o)s[t.status]=(s[t.status]||0)+1;console.log(`[bostrat-replay] ${o.length} task spec${o.length===1?"":"s"} on this node \u2014 ${s.approved} approved (the bench) \xB7 ${s.drafted} awaiting approval \xB7 ${s.dismissed} dismissed`);for(let t of o){let a=t.versions[t.versions.length-1],n=t.status==="approved"?"+":t.status==="drafted"?"?":"-";console.log(`${n} ${t.id} v${t.version}${t.approvedVersion!=null&&t.approvedVersion!==t.version?` (approved v${t.approvedVersion})`:""} ${`${t.source.repo}#${t.source.pr}`.padEnd(28)} ${(t.source.category||"\u2013").padEnd(14)} ${(t.source.model||t.source.provider||"\u2013").padEnd(20)} active ${p(t.source.activeMs).padStart(5)} tests ${String(t.acceptance.tests.length).padStart(2)} ${C(a.statement,60)}`)}}async function q(e){let o=e._[0];o||r(`spec show needs a <spec>
|
|
12
12
|
${d}`);let{spec:s}=await $(`/api/replay/specs/${encodeURIComponent(o)}`).catch(t=>r(t.message));if(e.json){console.log(JSON.stringify(s,null,2));return}k(s),y(s.source),console.log(` acceptance: ${s.acceptance.tests.length} test path${s.acceptance.tests.length===1?"":"s"} (of ${s.acceptance.files} files the PR changed)${s.versions[s.versions.length-1].suite?` \xB7 suite: ${s.versions[s.versions.length-1].suite}`:""}`);for(let t of s.acceptance.tests)console.log(` ${t}`);console.log(` versions (${s.versions.length}):`);for(let t of s.versions)console.log(` \u2500\u2500 v${t.v} \xB7 ${t.outcome}${t.v===s.approvedVersion?" \xB7 THE BENCH ENTRY":""} \xB7 ${new Date(t.createdAt).toISOString()} \xB7 by ${t.by?.agent||"the lead"} on Track \u201C${t.by?.trackName||t.by?.trackId||"\u2013"}\u201D${t.note?` \xB7 ${t.note}`:""}${t.suite?` \xB7 suite: ${t.suite}`:""}`),console.log(t.statement.split(`
|
|
13
13
|
`).map(a=>` \u2502 ${a}`).join(`
|
|
14
|
-
`));console.log(` original prompts stored verbatim: ${s.prompts.length} (the opening ask is what the statement replaces; follow-ups are not replayed) \u2014 \`spec show ${s.id} --json\` prints them`)}function T(e,o){let s=String(e??"").trim().match(/^(\d+(?:\.\d+)?)\s*(m|min|mins|minutes?|h|hr|hrs|hours?|d|days?)?$/i);s||r(`${o} wants a duration like 75m, 1.5h or 1d (got "${e}")`);let t=Number(s[1]),a=(s[2]||"m").toLowerCase(),n=a.startsWith("h")?t*36e5:a.startsWith("d")?t*8*36e5:t*6e4;return n>0||r(`${o} must be positive`),n}var b=e=>`$${(Number(e)||0).toFixed(2)}`,R={sealing:"sealing the clone",starting:"starting the box",running:"the agent is working",checking:"running the acceptance tests",done:"done"};function w(e,{head:o=!0}={}){if(o&&console.log(`[bostrat-replay] run ${e.id} \u2014 ${e.status==="running"?`${e.status} \xB7 ${R[e.phase]||e.phase}`:e.outcome} \u2014 ${e.source.repo}#${e.source.pr} \xB7 spec ${e.specId} v${e.version} \xB7 ${e.model||e.provider}${e.trackId?` \xB7 Track ${e.trackId}`:""}`),console.log(` agent-active ${p(e.activeMs)} \xB7 wall ${p(e.wallMs)} \xB7 ${b(e.usd)} notional \xB7 ${e.turns} turn${e.turns===1?"":"s"} \xB7 ${e.toolCalls} tool call${e.toolCalls===1?"":"s"} \xB7 ${e.questions} question${e.questions===1?"":"s"} answered "use your judgment"`),e.source.activeMs!=null&&console.log(` the source Track took agent-active ${p(e.source.activeMs)} \xB7 ${b(e.source.usd)} on ${e.source.model||"\u2013"} (conversational actuals \u2014 a different quantity from this single-shot run, D7)`),e.cappedBy&&console.log(` CAPPED by ${e.cappedBy} (caps: ${b(e.caps.usd)} \xB7 active ${p(e.caps.activeMs)} \xB7 wall ${p(e.caps.wallMs)} \xB7 ${e.caps.questions} questions) \u2014 partial time and cost recorded (R24)`),e.error&&console.log(` error: ${e.error}`),e.acceptance){let s=e.check?.tests||[];console.log(` acceptance: ${e.acceptance.toUpperCase()} \u2014 ${s.filter(t=>t.status==="pass").length}/${s.length} test path${s.length===1?"":"s"} pass${e.acceptance==="unverified"?" (no test could run: excluded from pass-rate figures, R11)":""}`);for(let t of s)console.log(` ${t.status.padEnd(7)} ${t.path}${t.runner?` (${t.runner}, ${p(t.ms)})`:""}${t.reason?` \u2014 ${t.reason}`:""}`);e.check?.suite&&console.log(` suite: ${e.check.suite.status} \u2014 ${e.check.suite.cmd} (${p(e.check.suite.ms)})`);for(let t of e.check?.deps||[])console.log(` deps: ${t.status} \u2014 ${t.cmd} in ${t.dir} (${p(t.ms)})`)}e.status==="finished"&&console.log(` record: ${e.dir}/result.json${e.patchBytes!=null?` \xB7 result.patch (${e.patchBytes} bytes)`:""}${e.cloneRemoved?" \xB7 clone removed":" \xB7 clone kept"}`),console.log(` ${e.label}`)}async function D(e){let o=e._[0];o||r(`run needs a <spec>
|
|
14
|
+
`));console.log(` original prompts stored verbatim: ${s.prompts.length} (the opening ask is what the statement replaces; follow-ups are not replayed) \u2014 \`spec show ${s.id} --json\` prints them`)}function T(e,o){let s=String(e??"").trim().match(/^(\d+(?:\.\d+)?)\s*(m|min|mins|minutes?|h|hr|hrs|hours?|d|days?)?$/i);s||r(`${o} wants a duration like 75m, 1.5h or 1d (got "${e}")`);let t=Number(s[1]),a=(s[2]||"m").toLowerCase(),n=a.startsWith("h")?t*36e5:a.startsWith("d")?t*8*36e5:t*6e4;return n>0||r(`${o} must be positive`),n}var b=e=>`$${(Number(e)||0).toFixed(2)}`,R={sealing:"sealing the clone",starting:"starting the box",running:"the agent is working",checking:"running the acceptance tests",closing:"archiving the box and pruning the clone",done:"done"};function w(e,{head:o=!0}={}){if(o&&console.log(`[bostrat-replay] run ${e.id} \u2014 ${e.status==="running"?`${e.status} \xB7 ${R[e.phase]||e.phase}`:e.outcome} \u2014 ${e.source.repo}#${e.source.pr} \xB7 spec ${e.specId} v${e.version} \xB7 ${e.model||e.provider}${e.trackId?` \xB7 Track ${e.trackId}`:""}`),console.log(` agent-active ${p(e.activeMs)} \xB7 wall ${p(e.wallMs)} \xB7 ${b(e.usd)} notional \xB7 ${e.turns} turn${e.turns===1?"":"s"} \xB7 ${e.toolCalls} tool call${e.toolCalls===1?"":"s"} \xB7 ${e.questions} question${e.questions===1?"":"s"} answered "use your judgment"`),e.source.activeMs!=null&&console.log(` the source Track took agent-active ${p(e.source.activeMs)} \xB7 ${b(e.source.usd)} on ${e.source.model||"\u2013"} (conversational actuals \u2014 a different quantity from this single-shot run, D7)`),e.cappedBy&&console.log(` CAPPED by ${e.cappedBy} (caps: ${b(e.caps.usd)} \xB7 active ${p(e.caps.activeMs)} \xB7 wall ${p(e.caps.wallMs)} \xB7 ${e.caps.questions} questions) \u2014 partial time and cost recorded (R24)`),e.error&&console.log(` error: ${e.error}`),e.acceptance){let s=e.check?.tests||[];console.log(` acceptance: ${e.acceptance.toUpperCase()} \u2014 ${s.filter(t=>t.status==="pass").length}/${s.length} test path${s.length===1?"":"s"} pass${e.acceptance==="unverified"?" (no test could run: excluded from pass-rate figures, R11)":""}`);for(let t of s)console.log(` ${t.status.padEnd(7)} ${t.path}${t.runner?` (${t.runner}, ${p(t.ms)})`:""}${t.reason?` \u2014 ${t.reason}`:""}`);e.check?.suite&&console.log(` suite: ${e.check.suite.status} \u2014 ${e.check.suite.cmd} (${p(e.check.suite.ms)})`);for(let t of e.check?.deps||[])console.log(` deps: ${t.status} \u2014 ${t.cmd} in ${t.dir} (${p(t.ms)})`)}e.status==="finished"&&console.log(` record: ${e.dir}/result.json${e.patchBytes!=null?` \xB7 result.patch (${e.patchBytes} bytes)`:""}${e.cloneRemoved?" \xB7 clone removed":" \xB7 clone kept"}`),console.log(` ${e.label}`)}async function D(e){let o=e._[0];o||r(`run needs a <spec>
|
|
15
15
|
${d}`);let s={};e["cap-usd"]!=null&&(s.usd=Number(e["cap-usd"]),s.usd>0||r("--cap-usd wants a positive dollar figure")),e["cap-active"]!=null&&(s.activeMs=T(e["cap-active"],"--cap-active")),e["cap-wall"]!=null&&(s.wallMs=T(e["cap-wall"],"--cap-wall"));let t;try{t=await h("POST","/api/replay/runs",{spec:o,provider:e.provider||"claude",model:e.model||null,caps:s,keep:!!e.keep})}catch(c){r(c.status===409?`${c.message} \u2014 \`bostrat-replay runs stop ${c.data?.runId||"<run>"}\` ends it`:c.message)}let a=t.run;if(e["no-wait"]){e.json?console.log(JSON.stringify(a,null,2)):(w(a),console.log(` running in the background \u2014 \`bostrat-replay runs show ${a.id}\` reads it`));return}let n=null;for(;a.status==="running";)!e.json&&a.phase!==n&&(n=a.phase,process.stderr.write(`[bostrat-replay] run ${a.id}: ${R[n]||n}${a.trackId?` (Track ${a.trackId})`:""}
|
|
16
16
|
`)),await new Promise(c=>setTimeout(c,1e4)),a=(await $(`/api/replay/runs/${encodeURIComponent(a.id)}`)).run;e.json?console.log(JSON.stringify(a,null,2)):w(a)}async function F(e){let o=e._[0];if(o==="show"||o==="stop"){let n=e._[1];if(n||r(`runs ${o} needs a <run>
|
|
17
17
|
${d}`),o==="stop"){await h("POST",`/api/replay/runs/${encodeURIComponent(n)}/stop`).catch(u=>r(u.message)),console.log(`[bostrat-replay] run ${n}: stop requested \u2014 it is recorded as capped (stop) once the turn closes, then checked`);return}let{run:c}=await $(`/api/replay/runs/${encodeURIComponent(n)}`).catch(u=>r(u.message));e.json?console.log(JSON.stringify(c,null,2)):w(c);return}o&&r(d);let{runs:s,live:t}=await $(`/api/replay/runs${e.status?`?status=${encodeURIComponent(e.status)}`:""}`);if(e.json){console.log(JSON.stringify({runs:s,live:t},null,2));return}if(!s.length){console.log("[bostrat-replay] no replay runs on this node yet \u2014 `bostrat-replay run <spec>` starts one (spec ls lists the approved specs)");return}let a={};for(let n of s)a[n.status==="running"?"running":n.outcome]=(a[n.status==="running"?"running":n.outcome]||0)+1;console.log(`[bostrat-replay] ${s.length} replay run${s.length===1?"":"s"} on this node \u2014 ${Object.entries(a).map(([n,c])=>`${n} ${c}`).join(" \xB7 ")}${t?` \xB7 live: ${t.id}`:""}`);for(let n of s){let c=n.status==="running"?"~":n.outcome==="passed"?"+":n.outcome==="failed"?"-":n.outcome==="capped"?"c":n.outcome==="unverified"?"?":"!";console.log(`${c} ${n.id} ${O(n.startedAt)} ${(n.status==="running"?`running/${n.phase}`:n.outcome).padEnd(11)} ${`${n.source.repo}#${n.source.pr}`.padEnd(28)} spec ${n.specId} v${n.version} ${(n.model||n.provider).padEnd(16)} active ${p(n.activeMs).padStart(5)} ${b(n.usd).padStart(7)} q ${n.questions} ${n.acceptance||"\u2013"}`)}console.log(` ${s[0].label}`)}var g=process.argv.slice(2),m=g.shift();try{if(m==="census")await x(f(g));else if(m==="spec"){let e=g.shift(),o=f(g);e==="frame"?await _(o):e==="draft"?await L(o):e==="revise"?await U(o):e==="ls"?await B(o):e==="show"?await q(o):r(d)}else m==="run"?await D(f(g)):m==="runs"?await F(f(g)):r(d)}catch(e){r(e?.message||String(e))}
|
package/bin/bostrat-ship
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var d=process.env.BOSTRAT_AGENT_URL||`http://127.0.0.1:${process.env.BOSTRAT_AGENT_PORT||3362}`,i="[bostrat-ship]";function
|
|
2
|
+
var d=process.env.BOSTRAT_AGENT_URL||`http://127.0.0.1:${process.env.BOSTRAT_AGENT_PORT||3362}`,i="[bostrat-ship]";function r(e){let a=/fetch failed|ECONNREFUSED/.test(String(e))?`the bostrat agent isn't reachable at ${d} \u2014 run this from inside a Track box`:e;console.error(`${i} ${a}`),process.exit(1)}var n=process.argv.slice(2),l=n.shift();if(l==="updated"){let e=t=>{let o=n.indexOf(t);return o>=0?n[o+1]:void 0},a=e("--pr");a!=null&&!/^\d+$/.test(String(a).replace(/^#/,""))&&r("--pr takes a PR number");try{let t=await fetch(`${d}/api/ship/updated`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cwd:process.cwd(),number:a!=null?Number(String(a).replace(/^#/,"")):null,rebased:n.includes("--rebased"),base:e("--base")??null})}),o=await t.json().catch(()=>({}));t.ok||r(o.error||`HTTP ${t.status}`);let c=o.result||{};c.skipped?console.log(`${i} ${c.reason} \u2014 no new card.`):console.log(`${i} update to #${c.number} reported (${String(c.sha).slice(0,7)}) \u2014 the "Updated PR" card with merge + archive posts below your closing message when this turn ends.`)}catch(t){r(t.message)}process.exit(0)}l!=="offer"&&r("usage: bostrat-ship offer [plan.json | -] | bostrat-ship updated [--pr N] [--rebased] [--base main]");var p=n.find(e=>!e.startsWith("--")),h=async()=>{let e=[];for await(let a of process.stdin)e.push(a);return Buffer.concat(e).toString("utf8")},s;try{let e=!p||p==="-"?await h():await(await import("node:fs/promises")).readFile(p,"utf8");s=e.trim()?JSON.parse(e):{}}catch{r("plan must be JSON (a file path, or piped on stdin) \u2014 see the `bostrat-ship` header for the shape")}s.ship||(s={label:s.label,why:s.why,ship:{actions:s.actions,toPr:s.toPr,rebase:s.rebase,base:s.base,repos:s.repos}});s.label=s.label||"Ship";s.trigger="now";Array.isArray(s.ship.actions)||(s.ship.actions=["commit","push","pr"]);try{let e=await fetch(`${d}/api/followups/offer`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cwd:process.cwd(),plan:s})}),a=await e.json().catch(()=>({}));e.ok||r(a.error||`HTTP ${e.status}`);let t=a.result||{};if(t.skipped)console.log(`${i} ${t.reason} \u2014 no card rendered.`);else{if(console.log(`${i} ship offer posted: ${t.offered.join(" + ")} on ${t.branch}${t.prState==="open"?" (PR already open)":""} \u2014 one-tap controls are in the Track chat.`),t.replaced&&console.log(" This REPLACED the ship offer you already posted this turn (its card is withdrawn). Offer once, as the last thing you do."),t.blueprint?.offered){let o=t.blueprint.counts||{};console.log(` Blueprint status card attached (${o.ready??0} ready \xB7 ${o.waiting??0} waiting) \u2014 do not list blueprint items, stages or readiness in your message.`)}console.log(" HOLD HERE: end your turn now. The go-ahead comes back as a chat message naming the steps to run.")}}catch(e){r(e.message)}
|
package/dist/cli.js
CHANGED
|
@@ -123,7 +123,7 @@ Run it? (Y/n)`,"Y")).toLowerCase().startsWith("n"))return n.write(`aborted \u201
|
|
|
123
123
|
redeploy: fly deploy ${N} -c ${Re.join(N,"fly.toml")} -a ${d} --ha=false
|
|
124
124
|
Vendor logins (Claude/Codex) are NOT set up here \u2014 you type those later, inside
|
|
125
125
|
boxes, and they land on this machine's volume, never on bostrat (box-auth-policy).
|
|
126
|
-
`),h(0)}var Os,ml,ur,rn,Ds=R(()=>{Os="wss://plane.bostrat.ai/v1/node",ml=fl(import.meta.url)("../package.json").version,ur=`ghcr.io/globocodes/bostrat-node:${ml}`;rn=(e,{stdin:t,exec:n}={})=>new Promise((r,o)=>{if(n)return n(e,{stdin:t}).then(r,o);let s=pl("fly",e,{timeout:6e5,maxBuffer:16*1024*1024},(a,l,h)=>a?o(Object.assign(a,{stdout:l,stderr:h})):r({stdout:l,stderr:h}));t!=null&&s.stdin?.end(t)})});function js(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!bl.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 Sl(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:js(o.from,"from",n),toMs:js(o.to,"to",n)}))}return t}var yl,bl,hm,mm,Cs=R(()=>{yl={"claude-opus-5-5":[{from:null,to:null,input:4,output:20,cacheWrite:8,cacheRead:.2,cacheReadX:.05,fast:{input:8,output:40,cacheWrite:16,cacheRead:.4},verifiedAt:"2026-09-28",note:"verified 2026-09-28 against platform.claude.com/docs/en/about-claude/pricing: $4/$20, 1h cache write $8 (2\xD7), cache hits $0.20 (0.05\xD7 \u2014 footnote 2), fast $8/$40; added before any fleet turn was recorded."}],"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-10-05",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; re-verified 2026-10-05 (live pricing page: $5/$25, 1h write $10, hits $0.50, fast $10/$50)."}],"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-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20; re-verified 2026-10-05 (live pricing page: $5/$25, 1h write $10, hits $0.50, fast $10/$50)."}],"claude-sonnet-5-5":[{from:null,to:"2026-10-11",input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-10-05",note:"verified 2026-10-05 against platform.claude.com/docs/en/about-claude/pricing: $2/$10, 1h cache write $4 (2\xD7), cache hits $0.20 (0.1\xD7 \u2014 footnote 2 then named Opus 5.5 only). Superseded 2026-10-12 when the page moved Sonnet 5.5 hits to 0.05\xD7 (window below)."},{from:"2026-10-12",to:null,input:2,output:10,cacheWrite:4,cacheRead:.1,cacheReadX:.05,verifiedAt:"2026-10-12",note:"verified 2026-10-12 against platform.claude.com/docs/en/about-claude/pricing: $2/$10, 1h cache write $4 (2\xD7), cache hits $0.10 (0.05\xD7 \u2014 footnote 2 now names Opus 5.5 AND Sonnet 5.5; the prompt-caching multiplier table agrees)."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-10-05",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; re-verified 2026-10-05 (live pricing page, footnote 3 still published)."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page), re-verified 2026-10-05 (live pricing page)."}],"claude-haiku-5-5":[{from:null,to:null,input:.1,output:.5,cacheWrite:.2,cacheRead:.01,long:{over:1e5,input:.5,output:2.5,cacheWrite:1,cacheRead:.05},verifiedAt:"2026-10-19",note:"verified 2026-10-19 against platform.claude.com/docs/en/about-claude/pricing: prompts up to 100,000 tokens $0.10/$0.50, 1h cache write $0.20 (2\xD7), cache hits $0.01 (0.1\xD7); prompts over 100,000 tokens (the `long` variant) $0.50/$2.50, 1h cache write $1 (2\xD7), cache hits $0.05 (0.1\xD7); added before any fleet turn was recorded."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page), re-verified 2026-10-05 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-10-26",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page); re-verified 2026-10-26 (live pricing page, footnote 1 unchanged)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend then; re-verified 2026-10-05 (live pricing page: $10/$50, 1h write $20, hits $1 at 0.1\xD7)."}],"gpt-5.5-pro":[{from:null,to:null,input:30,output:180,cacheWrite:30,cacheRead:30,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $30 in / $180 out, no cached-input or cache-write price published (both priced as plain input); added before any fleet turn was recorded."}],"gpt-5.5-cyber":[{from:null,to:null,input:12.5,output:75,cacheWrite:12.5,cacheRead:1.25,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing, Cyber models table): $12.50 in / $1.25 cached / $75 out, no cache-write price published (priced as plain input); added before any fleet turn was recorded."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-10-05",note:"verified 2026-07-03, re-verified 2026-08-17 and 2026-10-05 (developers.openai.com pricing: $5 / $0.50 cached / $30); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-10-12",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL); re-verified 2026-10-12 (live pricing page: $4 / $0.40 cached / $5 cache writes / $20, promo note unchanged)."}],"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-10-05",note:"verified 2026-08-17, re-verified 2026-10-05 (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-10-05",note:"verified 2026-08-17, re-verified 2026-10-05 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-10-26",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed); re-verified 2026-10-26 (live pricing page: $10 / $1 cached / $12.50 30m cache writes / $50, fast $20/$100 \u2014 the page now also lists a 12h cache-write tier at 2.5\xD7 input, not modelled: codex reports no cache-write class)."}],"gpt-6.1-sol":[{from:null,to:null,input:2,output:10,cacheWrite:2.5,cacheRead:.1,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $2 in / $0.10 cached / $2.50 cache writes (1.25\xD7) / $10 out; added before any fleet turn was recorded."}],"gpt-6-sol":[{from:null,to:null,input:2,output:10,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $2 in / $0.20 cached / $2.50 cache writes (1.25\xD7) / $10 out; added before any fleet turn was recorded."}],"gpt-6-luna":[{from:null,to:null,input:.1,output:.5,cacheWrite:.125,cacheRead:.01,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $0.10 in / $0.01 cached / $0.125 cache writes (1.25\xD7) / $0.50 out; added before any fleet turn was recorded."}]},bl=/^\d{4}-\d{2}-\d{2}$/;hm=Sl(yl),mm=Object.freeze({staleDays:90,leadDays:14,confirmDays:30})});var Bs=R(()=>{Cs()});import Ls from"node:fs/promises";import Hs from"node:path";import _l from"node:os";function Ks(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function Gs(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function zs(e){let t=new Date(Ks(e));return t.setDate(t.getDate()+7),t.getTime()}function Vs(e){let t=new Date(Gs(e));return t.setMonth(t.getMonth()+1),t.getTime()}function kl(e=Date.now()){for(let[t,n]of Object.entries($l)){let r=n.startOf(e);if(r!==on[t]){on[t]=r;for(let o of dr.values())o[t]=0;for(let o of Fs)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Fs.delete(o)}}}function Al(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 l=Number(a);if(!Number.isFinite(l)||l<=0)throw new Error(`${n}.${s} must be a positive number or null`);o[s]=l}Object.keys(o).length&&(t[n]=o)}return t}async function qs(e,{source:t="local",updatedAt:n=Date.now()}={}){qe={caps:Al(e),updatedAt:n,source:t};try{await Ls.mkdir(Ws,{recursive:!0}),await Ls.writeFile(Tl(),JSON.stringify(qe,null,2))}catch{}return El()}function El(e=Date.now()){kl(e);let t=new Set([...dr.keys(),...Object.keys(qe?.caps||{})]),n={};for(let r of t){let o=dr.get(r);n[r]={weeklyUsd:Us(o?.weekly||0),monthlyUsd:Us(o?.monthly||0)}}return{caps:qe?.caps||{},configured:!!qe,seeded:vl,updatedAt:qe?.updatedAt??null,spent:n,windows:{weekly:{start:on.weekly,resetsAt:zs(e)},monthly:{start:on.monthly,resetsAt:Vs(e)}},warnPct:xl}}var Ws,Tl,xl,$l,qe,vl,on,dr,Fs,Us,Ys=R(()=>{Qt();Bs();Ws=process.env.BOSTRAT_STATE_DIR||Hs.join(process.env.HOME||_l.tmpdir(),".bostrat"),Tl=()=>Hs.join(Ws,"usage-caps.json"),xl=.8;$l={weekly:{startOf:Ks,nextStart:zs,capKey:"weeklyUsd"},monthly:{startOf:Gs,nextStart:Vs,capKey:"monthlyUsd"}},qe=null,vl=!1,on={weekly:0,monthly:0},dr=new Map,Fs=new Set;Us=e=>Math.round(e*1e4)/1e4});function Qs(){return Object.fromEntries(Object.entries(Xs.tokens).filter(([,e])=>Zs(e)).map(([e,t])=>[e,t.access]))}function ei(){let e={};for(let[t,n]of Object.entries(Xs.tokens)){let r=Zs(n)?"set":n.needsLogin?"needs-login":n.refresh?"refreshing":"needs-login";e[t]={state:r,source:"broker",...n.expiresAt?{expiresAt:n.expiresAt}:{},...n.obtainedAt?{obtainedAt:n.obtainedAt}:{},refreshable:!!n.refresh,...n.lastError?{error:String(n.lastError).slice(0,200)}:{}}}for(let[t,n]of Js)e[t]||(e[t]={state:"login-pending",source:"broker",startedAt:n.startedAt,redirectUri:n.redirectUri});return e}var Em,Om,Xs,Js,Zs,ti,ni=R(()=>{Em=10*6e4,Om=5*6e4,Xs={tokens:{},clients:{}},Js=new Map,Zs=e=>typeof e?.access=="string"&&e.access&&!e.needsLogin&&(e.expiresAt==null||e.expiresAt>Date.now());ti=()=>[...Js.keys()]});import Ol from"node:fs";import ye from"node:fs/promises";import ci from"node:os";import Xe from"node:path";function ui(e){return hr.add(e),()=>hr.delete(e)}function sn(){return{entries:structuredClone(fr),at:Date.now()}}function Dl(e,t,n){if(e.transport==="http"){let o=Object.fromEntries(Object.entries(e.headers||{}).map(([s,a])=>[s,Ye(a,t)]));return{...n==="claude"?{type:"http"}:{},url:Ye(e.url,t),...Object.keys(o).length?{headers:o}:{}}}let r=Object.fromEntries((e.env||[]).map(o=>[o,t[o]]));return{...n==="claude"?{type:"stdio"}:{},command:e.command,args:(e.args||[]).map(o=>Ye(o,t)),...Object.keys(r).length?{env:r}:{}}}function jl(e,t){let n=[`[mcp_servers.${e.name}]`];if(e.transport==="http"){n.push(`url = ${Et(Ye(e.url,t))}`);let r=Object.fromEntries(Object.entries(e.headers||{}).map(([o,s])=>[o,Ye(s,t)]));Object.keys(r).length&&n.push(`http_headers = ${oi(r)}`)}else{n.push(`command = ${Et(e.command)}`),n.push(`args = [${(e.args||[]).map(o=>Et(Ye(o,t))).join(", ")}]`);let r=Object.fromEntries((e.env||[]).map(o=>[o,t[o]]));Object.keys(r).length&&n.push(`env = ${oi(r)}`)}return n.join(`
|
|
126
|
+
`),h(0)}var Os,ml,ur,rn,Ds=R(()=>{Os="wss://plane.bostrat.ai/v1/node",ml=fl(import.meta.url)("../package.json").version,ur=`ghcr.io/globocodes/bostrat-node:${ml}`;rn=(e,{stdin:t,exec:n}={})=>new Promise((r,o)=>{if(n)return n(e,{stdin:t}).then(r,o);let s=pl("fly",e,{timeout:6e5,maxBuffer:16*1024*1024},(a,l,h)=>a?o(Object.assign(a,{stdout:l,stderr:h})):r({stdout:l,stderr:h}));t!=null&&s.stdin?.end(t)})});function js(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!bl.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 Sl(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:js(o.from,"from",n),toMs:js(o.to,"to",n)}))}return t}var yl,bl,hm,mm,Cs=R(()=>{yl={"claude-opus-5-5":[{from:null,to:null,input:4,output:20,cacheWrite:8,cacheRead:.2,cacheReadX:.05,fast:{input:8,output:40,cacheWrite:16,cacheRead:.4},verifiedAt:"2026-09-28",note:"verified 2026-09-28 against platform.claude.com/docs/en/about-claude/pricing: $4/$20, 1h cache write $8 (2\xD7), cache hits $0.20 (0.05\xD7 \u2014 footnote 2), fast $8/$40; added before any fleet turn was recorded."}],"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-10-05",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; re-verified 2026-10-05 (live pricing page: $5/$25, 1h write $10, hits $0.50, fast $10/$50)."}],"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-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20; re-verified 2026-10-05 (live pricing page: $5/$25, 1h write $10, hits $0.50, fast $10/$50)."}],"claude-sonnet-5-5":[{from:null,to:"2026-10-11",input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-10-05",note:"verified 2026-10-05 against platform.claude.com/docs/en/about-claude/pricing: $2/$10, 1h cache write $4 (2\xD7), cache hits $0.20 (0.1\xD7 \u2014 footnote 2 then named Opus 5.5 only). Superseded 2026-10-12 when the page moved Sonnet 5.5 hits to 0.05\xD7 (window below)."},{from:"2026-10-12",to:null,input:2,output:10,cacheWrite:4,cacheRead:.1,cacheReadX:.05,verifiedAt:"2026-10-12",note:"verified 2026-10-12 against platform.claude.com/docs/en/about-claude/pricing: $2/$10, 1h cache write $4 (2\xD7), cache hits $0.10 (0.05\xD7 \u2014 footnote 2 now names Opus 5.5 AND Sonnet 5.5; the prompt-caching multiplier table agrees)."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-10-05",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; re-verified 2026-10-05 (live pricing page, footnote 3 still published)."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page), re-verified 2026-10-05 (live pricing page)."}],"claude-haiku-5-5":[{from:null,to:null,input:.1,output:.5,cacheWrite:.2,cacheRead:.01,long:{over:1e5,input:.5,output:2.5,cacheWrite:1,cacheRead:.05},verifiedAt:"2026-10-19",note:"verified 2026-10-19 against platform.claude.com/docs/en/about-claude/pricing: prompts up to 100,000 tokens $0.10/$0.50, 1h cache write $0.20 (2\xD7), cache hits $0.01 (0.1\xD7); prompts over 100,000 tokens (the `long` variant) $0.50/$2.50, 1h cache write $1 (2\xD7), cache hits $0.05 (0.1\xD7); added before any fleet turn was recorded."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page), re-verified 2026-10-05 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-10-26",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page); re-verified 2026-10-26 (live pricing page, footnote 1 unchanged)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-10-05",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend then; re-verified 2026-10-05 (live pricing page: $10/$50, 1h write $20, hits $1 at 0.1\xD7)."}],"gpt-5.5-pro":[{from:null,to:null,input:30,output:180,cacheWrite:30,cacheRead:30,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $30 in / $180 out, no cached-input or cache-write price published (both priced as plain input); added before any fleet turn was recorded."}],"gpt-5.5-cyber":[{from:null,to:null,input:12.5,output:75,cacheWrite:12.5,cacheRead:1.25,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing, Cyber models table): $12.50 in / $1.25 cached / $75 out, no cache-write price published (priced as plain input); added before any fleet turn was recorded."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-10-05",note:"verified 2026-07-03, re-verified 2026-08-17 and 2026-10-05 (developers.openai.com pricing: $5 / $0.50 cached / $30); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-10-12",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL); re-verified 2026-10-12 (live pricing page: $4 / $0.40 cached / $5 cache writes / $20, promo note unchanged)."}],"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-10-05",note:"verified 2026-08-17, re-verified 2026-10-05 (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-10-05",note:"verified 2026-08-17, re-verified 2026-10-05 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra-law":[{from:null,to:null,input:10,output:50,cacheWrite:10,cacheRead:1,verifiedAt:"2026-11-02",note:"verified 2026-11-02 (developers.openai.com pricing, Legal model configurations table): $10 in / $1 cached / $50 out, promotional 'at least through December 31, 2026', no cache-write price published (priced as plain input); added before any fleet turn was recorded."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-10-26",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed); re-verified 2026-10-26 (live pricing page: $10 / $1 cached / $12.50 30m cache writes / $50, fast $20/$100 \u2014 the page now also lists a 12h cache-write tier at 2.5\xD7 input, not modelled: codex reports no cache-write class)."}],"gpt-6.1-sol":[{from:null,to:null,input:2,output:10,cacheWrite:2.5,cacheRead:.1,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $2 in / $0.10 cached / $2.50 cache writes (1.25\xD7) / $10 out; added before any fleet turn was recorded."}],"gpt-6-sol":[{from:null,to:null,input:2,output:10,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $2 in / $0.20 cached / $2.50 cache writes (1.25\xD7) / $10 out; added before any fleet turn was recorded."}],"gpt-6-luna":[{from:null,to:null,input:.1,output:.5,cacheWrite:.125,cacheRead:.01,verifiedAt:"2026-10-05",note:"verified 2026-10-05 (developers.openai.com pricing): $0.10 in / $0.01 cached / $0.125 cache writes (1.25\xD7) / $0.50 out; added before any fleet turn was recorded."}]},bl=/^\d{4}-\d{2}-\d{2}$/;hm=Sl(yl),mm=Object.freeze({staleDays:90,leadDays:14,confirmDays:30})});var Bs=R(()=>{Cs()});import Ls from"node:fs/promises";import Hs from"node:path";import _l from"node:os";function Ks(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function Gs(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function zs(e){let t=new Date(Ks(e));return t.setDate(t.getDate()+7),t.getTime()}function Vs(e){let t=new Date(Gs(e));return t.setMonth(t.getMonth()+1),t.getTime()}function kl(e=Date.now()){for(let[t,n]of Object.entries($l)){let r=n.startOf(e);if(r!==on[t]){on[t]=r;for(let o of dr.values())o[t]=0;for(let o of Fs)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Fs.delete(o)}}}function Al(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 l=Number(a);if(!Number.isFinite(l)||l<=0)throw new Error(`${n}.${s} must be a positive number or null`);o[s]=l}Object.keys(o).length&&(t[n]=o)}return t}async function qs(e,{source:t="local",updatedAt:n=Date.now()}={}){qe={caps:Al(e),updatedAt:n,source:t};try{await Ls.mkdir(Ws,{recursive:!0}),await Ls.writeFile(Tl(),JSON.stringify(qe,null,2))}catch{}return El()}function El(e=Date.now()){kl(e);let t=new Set([...dr.keys(),...Object.keys(qe?.caps||{})]),n={};for(let r of t){let o=dr.get(r);n[r]={weeklyUsd:Us(o?.weekly||0),monthlyUsd:Us(o?.monthly||0)}}return{caps:qe?.caps||{},configured:!!qe,seeded:vl,updatedAt:qe?.updatedAt??null,spent:n,windows:{weekly:{start:on.weekly,resetsAt:zs(e)},monthly:{start:on.monthly,resetsAt:Vs(e)}},warnPct:xl}}var Ws,Tl,xl,$l,qe,vl,on,dr,Fs,Us,Ys=R(()=>{Qt();Bs();Ws=process.env.BOSTRAT_STATE_DIR||Hs.join(process.env.HOME||_l.tmpdir(),".bostrat"),Tl=()=>Hs.join(Ws,"usage-caps.json"),xl=.8;$l={weekly:{startOf:Ks,nextStart:zs,capKey:"weeklyUsd"},monthly:{startOf:Gs,nextStart:Vs,capKey:"monthlyUsd"}},qe=null,vl=!1,on={weekly:0,monthly:0},dr=new Map,Fs=new Set;Us=e=>Math.round(e*1e4)/1e4});function Qs(){return Object.fromEntries(Object.entries(Xs.tokens).filter(([,e])=>Zs(e)).map(([e,t])=>[e,t.access]))}function ei(){let e={};for(let[t,n]of Object.entries(Xs.tokens)){let r=Zs(n)?"set":n.needsLogin?"needs-login":n.refresh?"refreshing":"needs-login";e[t]={state:r,source:"broker",...n.expiresAt?{expiresAt:n.expiresAt}:{},...n.obtainedAt?{obtainedAt:n.obtainedAt}:{},refreshable:!!n.refresh,...n.lastError?{error:String(n.lastError).slice(0,200)}:{}}}for(let[t,n]of Js)e[t]||(e[t]={state:"login-pending",source:"broker",startedAt:n.startedAt,redirectUri:n.redirectUri});return e}var Em,Om,Xs,Js,Zs,ti,ni=R(()=>{Em=10*6e4,Om=5*6e4,Xs={tokens:{},clients:{}},Js=new Map,Zs=e=>typeof e?.access=="string"&&e.access&&!e.needsLogin&&(e.expiresAt==null||e.expiresAt>Date.now());ti=()=>[...Js.keys()]});import Ol from"node:fs";import ye from"node:fs/promises";import ci from"node:os";import Xe from"node:path";function ui(e){return hr.add(e),()=>hr.delete(e)}function sn(){return{entries:structuredClone(fr),at:Date.now()}}function Dl(e,t,n){if(e.transport==="http"){let o=Object.fromEntries(Object.entries(e.headers||{}).map(([s,a])=>[s,Ye(a,t)]));return{...n==="claude"?{type:"http"}:{},url:Ye(e.url,t),...Object.keys(o).length?{headers:o}:{}}}let r=Object.fromEntries((e.env||[]).map(o=>[o,t[o]]));return{...n==="claude"?{type:"stdio"}:{},command:e.command,args:(e.args||[]).map(o=>Ye(o,t)),...Object.keys(r).length?{env:r}:{}}}function jl(e,t){let n=[`[mcp_servers.${e.name}]`];if(e.transport==="http"){n.push(`url = ${Et(Ye(e.url,t))}`);let r=Object.fromEntries(Object.entries(e.headers||{}).map(([o,s])=>[o,Ye(s,t)]));Object.keys(r).length&&n.push(`http_headers = ${oi(r)}`)}else{n.push(`command = ${Et(e.command)}`),n.push(`args = [${(e.args||[]).map(o=>Et(Ye(o,t))).join(", ")}]`);let r=Object.fromEntries((e.env||[]).map(o=>[o,t[o]]));Object.keys(r).length&&n.push(`env = ${oi(r)}`)}return n.join(`
|
|
127
127
|
`)+`
|
|
128
128
|
|
|
129
129
|
`}function di(e){let t=[],n=`(?:[A-Za-z0-9_-]+|"[^"
|