bostrat-node 1.16.0 → 1.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bostrat-audit +3 -3
- package/dist/cli.js +38 -38
- package/dist/index.js +343 -296
- package/package.json +1 -1
package/bin/bostrat-audit
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import j from"node:fs/promises";import $e from"node:os";import A from"node:path";var ne=(e,o)=>{if(e==null||e==="")return o;let r=Number(e);return Number.isFinite(r)&&r>=0?r:o},Te=ne(process.env.BOSTRAT_HOOK_FOLD_MS,250);var se=1e3;function N(e){let o=Number(e?.count);return Number.isInteger(o)&&o>1&&e?.tools&&typeof e.tools=="object"?Math.min(o,se):1}function D(e){let o=e||{},r=N(o);if(r===1)return[o];let i=(Array.isArray(o.calls)?o.calls:[]).filter(a=>a&&typeof a=="object").slice(0,r),n={};for(let a of i)a.tool&&(n[a.tool]=(n[a.tool]||0)+1);for(let[a,c]of Object.entries(o.tools))for(let p=n[a]||0;p<Number(c)&&i.length<r;p++)i.push({tool:a});for(;i.length<r;)i.push({tool:null});return i}var _=6e4,re=120*_,G=2e3,X=5e3,ie=2*_,ae=5e3,ce=2*_,F=5e3,le={Workflow:360*_,Agent:60*_},de=5*_,q=80,T=3,pe=["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"],H={operator:"strong",orchestrator:"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"},E=e=>String(e??"").replace(/\s+/g," ").trim(),d=e=>e?.ts??0,P=(e,o)=>!!e&&!!o&&(e.startsWith(o.slice(0,q))||o.startsWith(e.slice(0,q))),ue=6,fe=(e,o)=>P(e,o)||e.length>=ue&&o.includes(e.slice(0,q)),ge=/^<pasted_content\b[^>]*>\s*/i,C=e=>E(e.payload?.promptHead??e.payload?.text).replace(ge,""),he=/^<cross-session-message\b/i,me=/^<task-notification>/i,ye=/<tool-use-id>\s*([^<\s]+)\s*<\/tool-use-id>/i;function be(e){let o=e.payload||{},r=N(o),i=D(o)[0],n=i.head||i.path||(o.command?E(o.command):"");return`${i.tool||"tool"}${n?`: ${n.slice(0,100)}`:""}${r>1?` (+${r-1} more)`:""}`}function we(e){let o={events:e.length,firstSeq:null,lastSeq:null,headMissing:!1,gaps:0,repeats:0},r=null;for(let i of e)Number.isFinite(i?.seq)&&(r===null?o.firstSeq=i.seq:i.seq>r+1?o.gaps++:i.seq<=r&&o.repeats++,r=i.seq);return o.lastSeq=r,o.headMissing=o.firstSeq!==null&&o.firstSeq>1,o}function ke(e){let o=new Map,r=new Map,i=new Map,n=new Map,a=(s,g,b)=>{s.has(g)||s.set(g,[]),s.get(g).push(b)};for(let s of e)if((s.type==="box.state.changed"||s.type.startsWith("handoff."))&&a(n,s.payload?.box||s.boxId||null,d(s)),s.type==="session.ready"&&([...n.get(s.boxId)||[],...n.get(null)||[]].some(b=>b<=d(s)&&d(s)-b<=ce)||a(i,s.boxId,d(s))),s.type==="turn.user")if(s.source==="orchestrator"){let g=(s.payload?.attachments||[]).map(b=>b?.id).filter(Boolean);a(o,s.boxId,{ts:d(s),head:E(s.payload?.text),files:g,actor:s.actor,usedAt:null})}else s.source==="transcript"&&a(r,s.boxId,{ts:d(s),head:E(s.payload?.text),note:s.payload?.note?.kind||null});let c=new Map,p=new Map,h=new Map;for(let s of e){if(s.type==="turn.user"&&s.source==="orchestrator"&&p.set(s.boxId,!0),s.source!=="hook")continue;if(s.type==="turn.agent"){let k=h.get(s.boxId)||0;k>0?h.set(s.boxId,k-1):p.set(s.boxId,!1);continue}if(s.type!=="turn.user")continue;let g=C(s),b=(()=>{if(s.actor==="peer"||he.test(g))return{origin:"peer"};if(me.test(g))return{origin:"task-continuation",jobId:g.match(ye)?.[1]||null};let k=(o.get(s.boxId)||[]).filter(u=>(u.usedAt===null||d(s)-u.usedAt<=X)&&u.ts>=d(s)-re&&u.ts<=d(s)+G&&(fe(u.head,g)||!u.head&&u.files.length>0&&(u.files.some(t=>g.includes(`.bostrat/uploads/${t}`))||d(s)-u.ts<=X))),S=k[k.length-1];if(S)return S.usedAt=d(s),{origin:S.actor==="orchestrator"?"orchestrator":"operator"};if(pe.some(u=>g.startsWith(u)))return{origin:"orchestrator-unlogged"};if((r.get(s.boxId)||[]).find(u=>!u.note&&u.ts>=d(s)-G&&u.ts<=d(s)+ie&&P(u.head,g)))return{origin:"operator-terminal"};let $=(i.get(s.boxId)||[]).some(u=>u<=d(s)&&d(s)-u<=ae);return{origin:p.get(s.boxId)||$?"nested-session":"unknown-prompt"}})();c.set(s,b),b.origin==="nested-session"?h.set(s.boxId,(h.get(s.boxId)||0)+1):p.set(s.boxId,!0)}return c}function xe(e){let o=new Map,r=new Map;for(let i of e){let n=i.payload?.id;if(n)if(i.type==="job.started"){let a={from:(i.payload?.ts??d(i))-F,to:1/0,loggedAt:d(i)};r.set(n,a),o.has(i.boxId)||o.set(i.boxId,[]),o.get(i.boxId).push(a)}else i.type==="job.stopped"&&r.has(n)&&(r.get(n).to=d(i)+F)}return{byBox:o,byId:r}}function Se(e,{trackId:o=null,name:r=null,since:i=0}={}){let n=ke(e),a=xe(e),c=new Set;for(let t of e)t.type==="tool.called"&&t.source==="hook"&&c.add(t.boxId);let p=t=>t.type==="tool.called"&&(t.source==="hook"||!(t.provider==="claude"&&c.has(t.boxId))),h={},s={},g=new Map,b=(t,l,w,I,m,M=1)=>{let f=`${t}\0${w.boxId}\0${I}`,y=g.get(f);return(!y||d(w)-y.to>de)&&(y={rule:t,severity:l,trackId:o,track:r,boxId:w.boxId,from:d(w),to:d(w),count:0,note:m,samples:[]},g.set(f,y),k.push(y)),y.to=d(w),y.count+=M,y},k=[],S=new Map,K=t=>(S.has(t)||S.set(t,{open:!1,origin:null,nested:0,soft:null,lastSendHead:null,launch:null}),S.get(t));for(let t of e){let l=K(t.boxId),w=d(t)>=i;if(t.type==="session.foreign"){if(t.payload?.phase==="prompted"&&w){let f=b("nested-session","medium",t,"","a session that is not the box's own was prompted through this box \u2014 typically an agent-launched `claude -p`");f.samples.length<T&&f.samples.push(E(t.payload?.promptHead).slice(0,100))}continue}if(t.type==="turn.user"&&t.source==="hook"){let f=n.get(t);if(f.origin==="nested-session"){if(l.nested++,w){let y=b("nested-session","medium",t,"","a prompt no recorded send accounts for arrived mid-turn or right after a SessionStart \u2014 typically an agent-launched `claude -p`");y.samples.length<T&&y.samples.push(C(t).slice(0,100))}continue}if(l.open=!0,l.origin=f.origin,f.origin==="task-continuation"&&!a.byId.has(f.jobId)&&(l.origin="task-continuation-unlogged"),l.origin==="unknown-prompt"&&w){let y=b("unknown-prompt","medium",t,"","a turn opened by a prompt with no operator, machinery or job record behind it");y.samples.length<T&&y.samples.push(C(t).slice(0,100))}}else if(t.type==="turn.agent"&&t.source==="hook")l.nested>0?l.nested--:l.open=!1;else if(t.type==="turn.aborted"||t.type==="turn.interrupted")l.open=!1;else if(t.type==="turn.user"&&t.source==="orchestrator")l.lastSendHead=E(t.payload?.text),l.soft=t.actor==="orchestrator"?"orchestrator":"operator",l.open=!0,l.origin=l.soft;else if(t.type==="turn.user"&&t.source==="transcript"){let f=E(t.payload?.text);!t.payload?.note&&!P(f,l.lastSendHead)&&(l.soft="operator-terminal")}else t.type==="peer.message"&&(l.soft="peer");if(!p(t))continue;let I=t.source==="hook"?N(t.payload):1,m;if(t.payload?.foreignSession)m="nested-session";else if(t.source==="hook"){let f=(a.byBox.get(t.boxId)||[]).some(x=>d(t)>=x.from&&d(t)<=x.to);l.open?m=l.origin:f?m="background-job":l.nested>0?m="nested-session":l.launch&&d(t)<=l.launch?m="background-launch":m="unrooted";let y=(a.byBox.get(t.boxId)||[]).some(x=>x.loggedAt<=d(t)+F);for(let x of new Set(D(t.payload).map(Y=>Y.tool)))(x==="Workflow"||x==="Agent"&&!y)&&l.open&&H[l.origin]!=="flagged"&&(l.launch=Math.max(l.launch||0,d(t)+le[x]))}else m=l.soft||"unrooted";if(!w)continue;h[m]=(h[m]||0)+I;let M=t.provider||"unknown";if(s[M]||={actions:0,fidelity:t.source==="hook"?"hook":"transcript"},s[M].actions+=I,t.source==="hook"&&(s[M].fidelity="hook"),m==="unrooted"){let f=b("unrooted-actions","high",t,"","tool calls with no open turn, no recorded job and no launch that accounts for them",I);f.samples.length<T&&f.samples.push(be(t))}else if(m==="peer")b("peer-driven-actions","low",t,"","tool calls in a turn another session's message opened",I);else if(m==="nested-session"||m==="unknown-prompt"){let f=g.get(`${m}\0${t.boxId}\0`);f&&(f.actions=(f.actions||0)+I)}}let $=we(e),u=Object.values(h).reduce((t,l)=>t+l,0);return $.headMissing&&u&&k.push({rule:"log-head-missing",severity:"medium",trackId:o,track:r,boxId:null,from:d(e[0]),to:d(e[0]),count:1,samples:[],note:`the log starts at seq ${$.firstSeq} \u2014 its earlier history is not on this node`}),{trackId:o,track:r,actions:u,byOrigin:h,byProvider:s,findings:k,integrity:$}}var V={high:0,medium:1,low:2};function Q(e,{days:o=14,now:r=Date.now()}={}){let i=o?r-o*864e5:0,n={days:o||null,tracks:0,actions:0,byOrigin:{},byStrength:{strong:0,weak:0,flagged:0},byProvider:{},findings:[],integrity:{logs:0,headMissing:0,withGaps:0,withRepeats:0}};for(let a of e||[]){let c=Se(a.events||[],{trackId:a.trackId,name:a.name,since:i});if(n.integrity.logs++,c.integrity.headMissing&&n.integrity.headMissing++,c.integrity.gaps&&n.integrity.withGaps++,c.integrity.repeats&&n.integrity.withRepeats++,!(!c.actions&&!c.findings.length)){n.tracks++,n.actions+=c.actions;for(let[p,h]of Object.entries(c.byOrigin))n.byOrigin[p]=(n.byOrigin[p]||0)+h,n.byStrength[H[p]||"flagged"]+=h;for(let[p,h]of Object.entries(c.byProvider))n.byProvider[p]||={actions:0,fidelity:h.fidelity},n.byProvider[p].actions+=h.actions,h.fidelity==="hook"&&(n.byProvider[p].fidelity="hook");n.findings.push(...c.findings)}}return n.findings.sort((a,c)=>V[a.severity]-V[c.severity]||c.to-a.to),n.ok=!n.findings.some(a=>a.severity==="high"),n}var z=(e,o)=>o?`${(e/o*100).toFixed(e&&e/o<.001?2:1)}%`:"0%",R=e=>new Date(e).toISOString().slice(0,16).replace("T"," ");function Z(e,{limit:o=20}={}){let r=[];r.push(`AGENT ACTION AUDIT \xB7 ${e.days?`last ${e.days}d`:"all history"} \xB7 ${e.actions} action(s) across ${e.tracks} Track(s)`),r.push(""),r.push("Who asked for it");for(let n of["strong","weak","flagged"]){let a=Object.entries(e.byOrigin).filter(([c])=>(H[c]||"flagged")===n).sort((c,p)=>p[1]-c[1]);if(a.length){r.push(` ${n.toUpperCase()} \xB7 ${e.byStrength[n]} (${z(e.byStrength[n],e.actions)})`);for(let[c,p]of a)r.push(` ${c.padEnd(27)} ${String(p).padStart(7)} ${z(p,e.actions)}`)}}r.push(""),r.push("Evidence by provider");for(let[n,a]of Object.entries(e.byProvider).sort((c,p)=>p[1].actions-c[1].actions))r.push(` ${n.padEnd(10)} ${String(a.actions).padStart(7)} `+(a.fidelity==="hook"?"hook-recorded (turn boundaries known)":"transcript only (agent-written; no turn boundaries)"));r.push("");let i=e.integrity;if(r.push(`Log integrity \xB7 ${i.logs} log(s): ${i.headMissing} missing their head, ${i.withGaps} with seq gaps, ${i.withRepeats} with repeated seq (informational until the hash chain lands)`),r.push(""),!e.findings.length)r.push("Findings: none.");else{r.push(`Findings \xB7 ${e.findings.length}`);for(let n of e.findings.slice(0,o)){let a=n.to-n.from>_?`${R(n.from)} \u2192 ${R(n.to).slice(11)}`:R(n.from),c=n.actions?`${n.count} prompt(s), ${n.actions} action(s)`:`${n.count}`;r.push(` [${n.severity}] ${n.rule} \xB7 ${n.track||n.trackId}${n.boxId?` \xB7 ${n.boxId}`:""} \xB7 ${a} \xB7 ${c}`),r.push(` ${n.note}`);for(let p of n.samples)r.push(` - ${p}`)}e.findings.length>o&&r.push(` \u2026 ${e.findings.length-o} more (--json for all)`)}return r.push(""),r.push(e.ok?"No action without an origin. The log is agent-writable, so this is evidence, not proof.":"Actions with NO origin were found \u2014 read the [high] findings above."),r.join(`
|
|
3
|
-
`)}import
|
|
4
|
-
`))if(c.trim())try{
|
|
2
|
+
import L from"node:fs/promises";import Ze from"node:os";import v from"node:path";var ke=(e,o)=>{if(e==null||e==="")return o;let t=Number(e);return Number.isFinite(t)&&t>=0?t:o},ot=ke(process.env.BOSTRAT_HOOK_FOLD_MS,250);var we=1e3;function U(e){let o=Number(e?.count);return Number.isInteger(o)&&o>1&&e?.tools&&typeof e.tools=="object"?Math.min(o,we):1}function q(e){let o=e||{},t=U(o);if(t===1)return[o];let a=(Array.isArray(o.calls)?o.calls:[]).filter(i=>i&&typeof i=="object").slice(0,t),r={};for(let i of a)i.tool&&(r[i.tool]=(r[i.tool]||0)+1);for(let[i,c]of Object.entries(o.tools))for(let p=r[i]||0;p<Number(c)&&a.length<t;p++)a.push({tool:i});for(;a.length<t;)a.push({tool:null});return a}import{createHash as Ie}from"node:crypto";var xe=new Set(["userId"]);function K(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(t=>K(t===void 0?null:t)).join(",")}]`:`{${Object.keys(e).filter(t=>e[t]!==void 0).sort().map(t=>`${JSON.stringify(t)}:${K(e[t])}`).join(",")}}`}function se(e){let o={};for(let t of Object.keys(e))xe.has(t)||(o[t]=e[t]);return Ie("sha256").update(K(o)).digest("hex")}var re=e=>Number.isFinite(e?.seq);function ie(e){let o={members:0,chained:0,preChain:0,intactFrom:null,intactTo:null,genesis:!1,breaks:[]},t=null,a=!1,r=(i,c)=>{t={seq:i.seq,hash:c},o.intactFrom=i.seq,o.genesis=i.seq===1&&i.prev===null};for(let i of e||[]){if(!re(i))continue;if(o.members++,i.prev===void 0){t&&!a?(o.breaks.push({kind:"unchained",seq:i.seq,eventId:i.id??null}),a=!0):t||o.preChain++;continue}o.chained++;let c=se(i);if(!t||a){a=!1,r(i,c);continue}if(i.seq===t.seq+1){if(i.prev===t.hash){t={seq:i.seq,hash:c};continue}o.breaks.push({kind:"prev-mismatch",seq:i.seq,eventId:i.id??null,expected:t.hash,got:i.prev})}else i.seq>t.seq+1?o.breaks.push({kind:"seq-gap",seq:i.seq,eventId:i.id??null,from:t.seq+1,to:i.seq-1}):o.breaks.push({kind:"seq-repeat",seq:i.seq,eventId:i.id??null,headSeq:t.seq});r(i,c)}return o.intactTo=t?t.seq:null,t||(o.intactFrom=null),o}var A=6e4,Se=120*A,ae=2e3,ce=5e3,$e=2*A,Ee=5e3,_e=2*A,C=5e3,Me={Workflow:360*A,Agent:60*A},Ae=5*A,Oe=10*A,J=80,T=3,qe=["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"],X={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"},M=e=>String(e??"").replace(/\s+/g," ").trim(),u=e=>e?.ts??0,V=(e,o)=>!!e&&!!o&&(e.startsWith(o.slice(0,J))||o.startsWith(e.slice(0,J))),Te=6,ve=(e,o)=>V(e,o)||e.length>=Te&&o.includes(e.slice(0,J)),je=/^<pasted_content\b[^>]*>\s*/i,G=e=>M(e.payload?.promptHead??e.payload?.text).replace(je,""),Ne=/^<cross-session-message\b/i,Fe=/^<task-notification>/i,Ce=/<tool-use-id>\s*([^<\s]+)\s*<\/tool-use-id>/i;function de(e){let o=e.payload||{},t=U(o),a=q(o)[0],r=a.head||a.path||(o.command?M(o.command):""),i=e.type==="tool.failed"?" [failed]":e.type==="tool.started"?" [never completed]":"";return`${a.tool||"tool"}${r?`: ${r.slice(0,100)}`:""}${i}${t>1?` (+${t-1} more)`:""}`}var De=/\.answered$/,Re=new Set(["loop.created","followup.created"]),Ue=16,ue=new Set(["tool.started","tool.called","tool.failed"]);function Pe(){return{byId:new Map,byDelivery:new Map,byPrompt:new Map,byToolUse:new Map,jobs:new Map}}function Le(e,o){if(!o||typeof o.type!="string")return;o.id&&e.byId.set(o.id,o);let t=o.payload||{};if(o.type==="turn.user")o.source==="orchestrator"&&t.deliveryId&&e.byDelivery.set(t.deliveryId,o),o.source==="hook"&&t.promptId&&!e.byPrompt.has(`${o.boxId}\0${t.promptId}`)&&e.byPrompt.set(`${o.boxId}\0${t.promptId}`,o);else if(o.type==="job.started"&&t.id)e.jobs.set(t.id,o);else if(ue.has(o.type)&&o.source==="hook")for(let a of q(t))a.toolUseId&&!e.byToolUse.has(a.toolUseId)&&e.byToolUse.set(a.toolUseId,o)}function He(e){let o=Pe();for(let t of e)Le(o,t);return o}function le(e,o){let t=e;for(let a=0;t&&a<Ue;a++){let r=t.payload||{},i=t.type;if(t.causationId&&o.byId.has(t.causationId)&&t.causationId!==t.id){t=o.byId.get(t.causationId);continue}if(ue.has(i)){let c=r.promptId?o.byPrompt.get(`${t.boxId}\0${r.promptId}`):null;if(!c)return null;t=c;continue}if(i==="turn.user"){if(t.source==="hook"){if(r.kind==="task-notification"&&r.jobId){let p=o.jobs.get(r.jobId);if(!p)return null;t=p;continue}let c=r.deliveryId?o.byDelivery.get(r.deliveryId):null;if(!c)return null;t=c;continue}return t.source==="orchestrator"?r.loopId||r.producer==="loop"?{origin:"loop",root:t}:{origin:t.actor==="orchestrator"?"orchestrator":"operator",root:t}:t.source==="transcript"?{origin:"operator-terminal",root:t}:null}if(i==="job.started"){let c=r.id?o.byToolUse.get(r.id):null;if(!c)return null;t=c;continue}return De.test(i)||Re.has(i)?{origin:"card-answer",root:t}:i==="peer.message"?{origin:"peer",root:t}:null}return null}function We(e){let o={events:e.length,firstSeq:null,lastSeq:null,headMissing:!1,gaps:0,repeats:0},t=null;for(let r of e)Number.isFinite(r?.seq)&&(t===null?o.firstSeq=r.seq:r.seq>t+1?o.gaps++:r.seq<=t&&o.repeats++,t=r.seq);o.lastSeq=t,o.headMissing=o.firstSeq!==null&&o.firstSeq>1;let a=ie(e);return o.chain={state:a.chained===0?"pre-chain":a.breaks.length?"broken":a.genesis?"intact":"intact-from-mid-log",chained:a.chained,preChain:a.preChain,intactFrom:a.intactFrom,intactTo:a.intactTo,breaks:a.breaks},o}var Be=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 Ke(e){let o=new Map,t=new Map,a=new Map,r=new Map,i=(s,g,I)=>{s.has(g)||s.set(g,[]),s.get(g).push(I)};for(let s of e)if((s.type==="box.state.changed"||s.type.startsWith("handoff."))&&i(r,s.payload?.box||s.boxId||null,u(s)),s.type==="session.ready"&&([...r.get(s.boxId)||[],...r.get(null)||[]].some(I=>I<=u(s)&&u(s)-I<=_e)||i(a,s.boxId,u(s))),s.type==="turn.user")if(s.source==="orchestrator"){let g=(s.payload?.attachments||[]).map(I=>I?.id).filter(Boolean);i(o,s.boxId,{ts:u(s),head:M(s.payload?.text),files:g,actor:s.actor,usedAt:null})}else s.source==="transcript"&&i(t,s.boxId,{ts:u(s),head:M(s.payload?.text),note:s.payload?.note?.kind||null});let c=new Map,p=new Map,f=new Map;for(let s of e){if(s.type==="turn.user"&&s.source==="orchestrator"&&p.set(s.boxId,!0),s.source!=="hook")continue;if(s.type==="turn.agent"){let x=f.get(s.boxId)||0;x>0?f.set(s.boxId,x-1):p.set(s.boxId,!1);continue}if(s.type!=="turn.user")continue;let g=G(s),I=(()=>{if(s.actor==="peer"||Ne.test(g))return{origin:"peer"};if(Fe.test(g))return{origin:"task-continuation",jobId:g.match(Ce)?.[1]||null};let x=(o.get(s.boxId)||[]).filter(m=>(m.usedAt===null||u(s)-m.usedAt<=ce)&&m.ts>=u(s)-Se&&m.ts<=u(s)+ae&&(ve(m.head,g)||!m.head&&m.files.length>0&&(m.files.some(E=>g.includes(`.bostrat/uploads/${E}`))||u(s)-m.ts<=ce))),$=x[x.length-1];if($)return $.usedAt=u(s),{origin:$.actor==="orchestrator"?"orchestrator":"operator"};if(qe.some(m=>g.startsWith(m)))return{origin:"orchestrator-unlogged"};if((t.get(s.boxId)||[]).find(m=>!m.note&&m.ts>=u(s)-ae&&m.ts<=u(s)+$e&&V(m.head,g)))return{origin:"operator-terminal"};let D=(a.get(s.boxId)||[]).some(m=>m<=u(s)&&u(s)-m<=Ee);return{origin:p.get(s.boxId)||D?"nested-session":"unknown-prompt"}})();c.set(s,I),I.origin==="nested-session"?f.set(s.boxId,(f.get(s.boxId)||0)+1):p.set(s.boxId,!0)}return c}function Ye(e){let o=new Map,t=new Map;for(let a of e){let r=a.payload?.id;if(r)if(a.type==="job.started"){let i={from:(a.payload?.ts??u(a))-C,to:1/0,loggedAt:u(a)};t.set(r,i),o.has(a.boxId)||o.set(a.boxId,[]),o.get(a.boxId).push(i)}else a.type==="job.stopped"&&t.has(r)&&(t.get(r).to=u(a)+C)}return{byBox:o,byId:t}}function Je(e,{trackId:o=null,name:t=null,since:a=0}={}){let r=Ke(e),i=Ye(e),c=new Set;for(let n of e)n.type==="tool.called"&&n.source==="hook"&&c.add(n.boxId);let p=n=>n.type==="tool.called"&&(n.source==="hook"||!(n.provider==="claude"&&c.has(n.boxId)))||n.type==="tool.failed"&&n.source==="hook",f=new Set,s=new Map;for(let n of e)if(n.source==="hook")if(n.type==="tool.called"||n.type==="tool.failed")for(let l of q(n.payload))l.toolUseId&&f.add(l.toolUseId);else(n.type==="turn.agent"||n.type==="session.ready"||n.type==="turn.aborted"||n.type==="turn.interrupted")&&(s.has(n.boxId)||s.set(n.boxId,[]),s.get(n.boxId).push(u(n)));let g=e.length?u(e[e.length-1]):0,I=n=>{if(n.type!=="tool.started"||n.source!=="hook")return null;let l=q(n.payload).filter(b=>b.toolUseId&&!f.has(b.toolUseId));return l.length&&((s.get(n.boxId)||[]).some(b=>b>u(n))||g-u(n)>Oe)?l:null},x={},$={},W={keyed:0,timing:0},D=He(e),m=new Map,E=(n,l,d,b,k,h=1)=>{let j=`${n}\0${d.boxId}\0${b}`,w=m.get(j);return(!w||u(d)-w.to>Ae)&&(w={rule:n,severity:l,trackId:o,track:t,boxId:d.boxId,from:u(d),to:u(d),count:0,note:k,samples:[]},m.set(j,w),R.push(w)),w.to=u(d),w.count+=h,w},R=[],B=new Map,be=n=>(B.has(n)||B.set(n,{open:!1,origin:null,nested:0,soft:null,lastSendHead:null,launch:null}),B.get(n));for(let n of e){let l=be(n.boxId),d=u(n)>=a;if(n.type==="session.foreign"){if(n.payload?.phase==="prompted"&&d){let y=E("nested-session","medium",n,"","a session that is not the box's own was prompted through this box \u2014 typically an agent-launched `claude -p`");y.samples.length<T&&y.samples.push(M(n.payload?.promptHead).slice(0,100))}continue}if(n.type==="turn.user"&&n.source==="hook"){let y=r.get(n);if(y.origin==="nested-session"){if(l.nested++,d){let O=E("nested-session","medium",n,"","a prompt no recorded send accounts for arrived mid-turn or right after a SessionStart \u2014 typically an agent-launched `claude -p`");O.samples.length<T&&O.samples.push(G(n).slice(0,100))}continue}if(l.open=!0,l.origin=y.origin,y.origin==="task-continuation"&&!i.byId.has(y.jobId)&&(l.origin="task-continuation-unlogged"),l.origin==="unknown-prompt"&&d){let O=E("unknown-prompt","medium",n,"","a turn opened by a prompt with no operator, machinery or job record behind it");O.samples.length<T&&O.samples.push(G(n).slice(0,100))}}else if(n.type==="turn.agent"&&n.source==="hook")l.nested>0?l.nested--:l.open=!1;else if(n.type==="turn.aborted"||n.type==="turn.interrupted")l.open=!1;else if(n.type==="turn.user"&&n.source==="orchestrator")l.lastSendHead=M(n.payload?.text),l.soft=n.actor==="orchestrator"?"orchestrator":"operator",l.open=!0,l.origin=l.soft;else if(n.type==="turn.user"&&n.source==="transcript"){let y=M(n.payload?.text);!n.payload?.note&&!V(y,l.lastSendHead)&&(l.soft="operator-terminal")}else n.type==="peer.message"&&(l.soft="peer");let b=I(n);if(!b&&!p(n))continue;let k=b?b.length:n.source==="hook"?U(n.payload):1,h;if(n.payload?.foreignSession)h="nested-session";else if(n.source==="hook"){let y=(i.byBox.get(n.boxId)||[]).some(_=>u(n)>=_.from&&u(n)<=_.to);l.open?h=l.origin:y?h="background-job":l.nested>0?h="nested-session":l.launch&&u(n)<=l.launch?h="background-launch":h="unrooted";let O=(i.byBox.get(n.boxId)||[]).some(_=>_.loggedAt<=u(n)+C);for(let _ of new Set(q(n.payload).map(ne=>ne.tool)))(_==="Workflow"||_==="Agent"&&!O)&&l.open&&X[l.origin]!=="flagged"&&(l.launch=Math.max(l.launch||0,u(n)+Me[_]))}else h=l.soft||"unrooted";let j=n.source==="hook"&&!n.payload?.foreignSession?le(n,D):null;if(j&&(h=j.origin),!d)continue;W[j?"keyed":"timing"]+=k,x[h]=(x[h]||0)+k;let w=n.provider||"unknown";if($[w]||={actions:0,fidelity:n.source==="hook"?"hook":"transcript",identified:!1},$[w].actions+=k,n.source==="hook"&&($[w].fidelity="hook"),n.source==="hook"&&q(n.payload).some(y=>y.toolUseId)&&($[w].identified=!0),b){let y=E("unfinished-calls","low",n,"","calls the CLI began (PreToolUse) and never reported complete or failed \u2014 killed, crashed or restarted mid-call; the tool may have run",k);y.samples.length<T&&y.samples.push(de(n))}if(h==="unrooted"){let y=E("unrooted-actions","high",n,"","tool calls with no open turn, no recorded job and no launch that accounts for them",k);y.samples.length<T&&y.samples.push(de(n))}else if(h==="peer")E("peer-driven-actions","low",n,"","tool calls in a turn another session's message opened",k);else if(h==="nested-session"||h==="unknown-prompt"){let y=m.get(`${h}\0${n.boxId}\0`);y&&(y.actions=(y.actions||0)+k)}}let F={total:0,keyed:0,timing:0,unrooted:0};{let n=new Map,l=new Map;for(let d of e)d.source==="hook"&&(d.type==="turn.user"?l.set(d.boxId,u(d)):(d.type==="turn.agent"||d.type==="turn.aborted"||d.type==="turn.interrupted")&&l.has(d.boxId)&&((n.get(d.boxId)||n.set(d.boxId,[]).get(d.boxId)).push([l.get(d.boxId),u(d)]),l.delete(d.boxId)));for(let[d,b]of l)(n.get(d)||n.set(d,[]).get(d)).push([b,1/0]);for(let d of e){if(d.type!=="code.committed"||d.source!=="hook"||u(d)<a)continue;F.total++;let b=Number(d.payload?.ts??d.ts)||u(d);if(le(d,D))F.keyed++;else if((n.get(d.boxId)||[]).some(([k,h])=>b>=k-C&&b<=h+C))F.timing++;else{F.unrooted++;let k=E("unrooted-commit","medium",d,"","a commit landed in the box's workspace with no turn open and no recorded cause");k.samples.length<T&&k.samples.push(`${String(d.payload?.sha||"").slice(0,7)} ${M(d.payload?.subject).slice(0,80)}`)}}}let S=We(e),oe=Object.values(x).reduce((n,l)=>n+l,0);if(S.headMissing&&oe&&R.push({rule:"log-head-missing",severity:"medium",trackId:o,track:t,boxId:null,from:u(e[0]),to:u(e[0]),count:1,samples:[],note:`the log starts at seq ${S.firstSeq} \u2014 its earlier history is not on this node`}),S.chain.breaks.length){let n=new Map;for(let h of e)Number.isFinite(h?.seq)&&!n.has(h.seq)&&n.set(h.seq,h);let l=S.chain.breaks.filter(h=>h.kind==="prev-mismatch"||h.kind==="seq-repeat"),d=h=>u(n.get(h.seq))||u(e.at(-1)),b=S.chain.breaks[0],k=S.chain.breaks.at(-1);R.push({rule:"chain-broken",severity:l.length?"high":"medium",trackId:o,track:t,boxId:null,from:d(b),to:d(k),count:S.chain.breaks.length,samples:S.chain.breaks.slice(0,T).map(Be),note:`the log's hash chain breaks ${S.chain.breaks.length} time(s) on this node \u2014 advisory: the plane's verified head is the anchor (GET /plane/admin/chain/${o??"<track>"})`})}return{trackId:o,track:t,actions:oe,byOrigin:x,byEvidence:W,byProvider:$,commits:F,findings:R,integrity:S}}var pe={high:0,medium:1,low:2};function fe(e,{days:o=14,now:t=Date.now()}={}){let a=o?t-o*864e5:0,r={days:o||null,tracks:0,actions:0,byOrigin:{},byStrength:{strong:0,weak:0,flagged:0},byEvidence:{keyed: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 i of e||[]){let c=Je(i.events||[],{trackId:i.trackId,name:i.name,since:a});r.integrity.logs++,c.integrity.headMissing&&r.integrity.headMissing++,c.integrity.gaps&&r.integrity.withGaps++,c.integrity.repeats&&r.integrity.withRepeats++;let p=c.integrity.chain?.state;if(p==="intact"?r.integrity.chain.intact++:p==="intact-from-mid-log"?r.integrity.chain.intactFromMidLog++:p==="broken"?r.integrity.chain.broken++:r.integrity.chain.preChain++,!(!c.actions&&!c.findings.length)){r.tracks++,r.actions+=c.actions;for(let f of Object.keys(r.byEvidence))r.byEvidence[f]+=c.byEvidence?.[f]||0;for(let f of Object.keys(r.commits))r.commits[f]+=c.commits?.[f]||0;for(let[f,s]of Object.entries(c.byOrigin))r.byOrigin[f]=(r.byOrigin[f]||0)+s,r.byStrength[X[f]||"flagged"]+=s;for(let[f,s]of Object.entries(c.byProvider))r.byProvider[f]||={actions:0,fidelity:s.fidelity,identified:!1},r.byProvider[f].actions+=s.actions,s.fidelity==="hook"&&(r.byProvider[f].fidelity="hook"),s.identified&&(r.byProvider[f].identified=!0);r.findings.push(...c.findings)}}return r.findings.sort((i,c)=>pe[i.severity]-pe[c.severity]||c.to-i.to),r.ok=!r.findings.some(i=>i.severity==="high"),r}var P=(e,o)=>o?`${(e/o*100).toFixed(e&&e/o<.001?2:1)}%`:"0%",Y=e=>new Date(e).toISOString().slice(0,16).replace("T"," ");function he(e,{limit:o=20}={}){let t=[];t.push(`AGENT ACTION AUDIT \xB7 ${e.days?`last ${e.days}d`:"all history"} \xB7 ${e.actions} action(s) across ${e.tracks} Track(s)`),t.push(""),t.push("Who asked for it");for(let p of["strong","weak","flagged"]){let f=Object.entries(e.byOrigin).filter(([s])=>(X[s]||"flagged")===p).sort((s,g)=>g[1]-s[1]);if(f.length){t.push(` ${p.toUpperCase()} \xB7 ${e.byStrength[p]} (${P(e.byStrength[p],e.actions)})`);for(let[s,g]of f)t.push(` ${s.padEnd(27)} ${String(g).padStart(7)} ${P(g,e.actions)}`)}}t.push("");let a=e.byEvidence||{keyed:0,timing:0};t.push(`Rooted by recorded key \xB7 ${a.keyed} (${P(a.keyed,e.actions)}) \xB7 by timing \xB7 ${a.timing} (${P(a.timing,e.actions)})`);let r=e.commits||{total:0,keyed:0,timing:0,unrooted:0};r.total&&t.push(`Commits \xB7 ${r.total}: ${r.keyed} cite their turn, ${r.timing} inside an open turn, ${r.unrooted} unrooted`),t.push(""),t.push("Evidence by provider");for(let[p,f]of Object.entries(e.byProvider).sort((s,g)=>g[1].actions-s[1].actions))t.push(` ${p.padEnd(10)} ${String(f.actions).padStart(7)} `+(f.fidelity==="hook"?`hook-recorded (turn boundaries known${f.identified?"; calls identified, intent and outcome paired":""})`:"transcript only (agent-written; no turn boundaries)"));t.push("");let i=e.integrity,c=i.chain||{intact:0,intactFromMidLog:0,broken:0,preChain:0};if(t.push(`Log integrity \xB7 ${i.logs} log(s): hash chain intact on ${c.intact} from seq 1 and ${c.intactFromMidLog} from mid-log, broken on ${c.broken}, ${c.preChain} pre-chain (written before the chain); ${i.headMissing} missing their head, ${i.withGaps} with seq gaps, ${i.withRepeats} with repeated seq`),t.push(" (the chain is walked over this node's copy \u2014 advisory; the plane's verified head is the anchor)"),t.push(""),!e.findings.length)t.push("Findings: none.");else{t.push(`Findings \xB7 ${e.findings.length}`);for(let p of e.findings.slice(0,o)){let f=p.to-p.from>A?`${Y(p.from)} \u2192 ${Y(p.to).slice(11)}`:Y(p.from),s=p.actions?`${p.count} prompt(s), ${p.actions} action(s)`:`${p.count}`;t.push(` [${p.severity}] ${p.rule} \xB7 ${p.track||p.trackId}${p.boxId?` \xB7 ${p.boxId}`:""} \xB7 ${f} \xB7 ${s}`),t.push(` ${p.note}`);for(let g of p.samples)t.push(` - ${g}`)}e.findings.length>o&&t.push(` \u2026 ${e.findings.length-o} more (--json for all)`)}return t.push(""),t.push(e.ok?"No action without an origin. The log is agent-writable, so this is evidence, not proof.":"Actions with NO origin were found \u2014 read the [high] findings above."),t.join(`
|
|
3
|
+
`)}import Ge from"node:path";import Xe from"node:os";import{EventEmitter as Ve}from"node:events";var ze=new Ve;ze.setMaxListeners(0);var ft=process.env.BOSTRAT_STATE_DIR||Ge.join(process.env.HOME||Xe.tmpdir(),".bostrat");var Qe=/(?<!\\)((?:\\\\)*)\\u0000/g,ge=e=>e.includes("\\u0000"),me=e=>e.replace(Qe,"$1\\ufffd");var H=process.env.BOSTRAT_STATE_DIR||v.join(process.env.HOME||Ze.tmpdir(),".bostrat"),N=process.argv.slice(2),Z=e=>{let o=N.indexOf(e);return o>=0?N[o+1]:null};(N.includes("--help")||N.includes("-h"))&&(console.log("usage: bostrat-audit [--days N | --all] [--track <id-or-name>] [--limit N] [--json]"),process.exit(0));var ee=[];try{ee=JSON.parse(await L.readFile(v.join(H,"tracks.json"),"utf8")).tracks||[]}catch{}var et=new Map(ee.map(e=>[e.id,e.name])),te=new Set([v.join(H,"events")]);for(let e of ee)e.cwd&&te.add(v.join(e.cwd,".bostrat","events"));try{for(let e of await L.readdir(v.join(H,"workspaces")))te.add(v.join(H,"workspaces",e,".bostrat","events"))}catch{}var z=Z("--track"),ye=[];for(let e of te){let o=[];try{o=(await L.readdir(e)).filter(t=>t.endsWith(".ndjson"))}catch{continue}for(let t of o){let a=t.slice(0,-7),r=et.get(a)||null;if(z&&a!==z&&!(r||"").toLowerCase().includes(z.toLowerCase()))continue;let i=[];for(let c of(await L.readFile(v.join(e,t),"utf8")).split(`
|
|
4
|
+
`))if(c.trim())try{i.push(JSON.parse(ge(c)?me(c):c))}catch{}i.length&&ye.push({trackId:a,name:r,events:i})}}var tt=N.includes("--all")?0:Number(Z("--days"))||14,Q=fe(ye,{days:tt});console.log(N.includes("--json")?JSON.stringify(Q,null,2):he(Q,{limit:Number(Z("--limit"))||20}));process.exit(Q.ok?0:2);
|