@tuent/sentinel 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -210,6 +210,16 @@ function validatePolicy(data) {
210
210
  }
211
211
  }
212
212
  }
213
+ if (enforcement.unknownTools !== void 0) {
214
+ if (enforcement.unknownTools !== "deny" && enforcement.unknownTools !== "warn") {
215
+ fail('enforcement.unknownTools must be "deny" or "warn"');
216
+ }
217
+ }
218
+ if (enforcement.allowUnknownTools !== void 0) {
219
+ if (!Array.isArray(enforcement.allowUnknownTools) || !enforcement.allowUnknownTools.every((t) => typeof t === "string")) {
220
+ fail("enforcement.allowUnknownTools must be an array of tool name strings");
221
+ }
222
+ }
213
223
  if (enforcement.baselineMaturity !== void 0) {
214
224
  if (typeof enforcement.baselineMaturity !== "object" || enforcement.baselineMaturity === null) {
215
225
  fail("enforcement.baselineMaturity must be an object");
@@ -371,7 +381,7 @@ function policyToRole(policy) {
371
381
  function policyToConfig(policy) {
372
382
  const config = {};
373
383
  if (policy.enforcement) {
374
- if (policy.enforcement.restrictAfter !== void 0 || policy.enforcement.quarantineAfter !== void 0 || policy.enforcement.promote !== void 0 || policy.enforcement.baselineMaturity !== void 0) {
384
+ if (policy.enforcement.restrictAfter !== void 0 || policy.enforcement.quarantineAfter !== void 0 || policy.enforcement.promote !== void 0 || policy.enforcement.baselineMaturity !== void 0 || policy.enforcement.unknownTools !== void 0 || policy.enforcement.allowUnknownTools !== void 0) {
375
385
  config.enforcement = {};
376
386
  if (policy.enforcement.restrictAfter !== void 0) {
377
387
  config.enforcement.restrictAfter = policy.enforcement.restrictAfter;
@@ -385,6 +395,12 @@ function policyToConfig(policy) {
385
395
  if (policy.enforcement.baselineMaturity !== void 0) {
386
396
  config.enforcement.baselineMaturity = policy.enforcement.baselineMaturity;
387
397
  }
398
+ if (policy.enforcement.unknownTools !== void 0) {
399
+ config.enforcement.unknownTools = policy.enforcement.unknownTools;
400
+ }
401
+ if (policy.enforcement.allowUnknownTools !== void 0) {
402
+ config.enforcement.allowUnknownTools = policy.enforcement.allowUnknownTools;
403
+ }
388
404
  }
389
405
  }
390
406
  if (policy.alerts) {
@@ -425,4 +441,4 @@ export {
425
441
  policyToRole,
426
442
  policyToConfig
427
443
  };
428
- //# sourceMappingURL=chunk-2FFMYSVC.js.map
444
+ //# sourceMappingURL=chunk-WLIDSTS4.js.map
package/dist/cli.js CHANGED
@@ -1,34 +1,34 @@
1
1
  #!/usr/bin/env node
2
- import{runInitClaudeCode as le}from"./chunk-3U3PKD4N.js";import{AgentProfileManager as H,AlertManager as ce,AuditTrail as A,BaselineBuilder as k,CorrelationDetector as de,DeviationDetector as B,FileStorageBackend as ge,ProfileStore as ue,ReportGenerator as fe,Sentinel as C,SentinelRunner as pe,generateFleetReport as he}from"./chunk-QFRDEISP.js";import"./chunk-CUJKNIKT.js";import"./chunk-FMZWHT4M.js";import"./chunk-6MHWJATS.js";import{loadPolicy as me}from"./chunk-2FFMYSVC.js";import{getOrCreateKeyPair as M}from"./chunk-NUXSUSYY.js";import{join as u}from"path";import{homedir as m}from"os";import{readFile as E,writeFile as b,access as K,mkdir as W}from"fs/promises";function we(e){const o=new Date(e);if(isNaN(o.getTime()))return"unknown";const t=Date.now()-o.getTime();if(t<0)return"just now";const a=Math.floor(t/6e4);if(a<1)return"just now";if(a<60)return`${a} minute${a===1?"":"s"} ago`;const s=Math.floor(a/60);if(s<24)return`${s} hour${s===1?"":"s"} ago`;const r=Math.floor(s/24);if(r<14)return`${r} day${r===1?"":"s"} ago`;const d=Math.floor(r/7);if(r<60)return`${d} week${d===1?"":"s"} ago`;if(r>=365)return"over a year ago";const c=Math.floor(r/30);return`${c} month${c===1?"":"s"} ago`}function ye(e,o){const n=new Date(o),t=isNaN(n.getTime())?1/0:Math.floor((Date.now()-n.getTime())/864e5);return e<.3||t>30?"declining":e>.7&&t<7?"rising":"stable"}function $e(e){return e<.3?"inner":e<.65?"middle":"outer"}function U(e){if(e.length===0)return"No petals selected.";const o=new Map;for(const t of e)o.set(t.id,t.label);const n=[`Selected petals (${e.length}):
3
- `];for(const t of e){const a=$e(t.layer),s=t.isRichData?"":" [filler]";n.push(`- ${t.label}${s}`),n.push(` Category: ${t.category}`),n.push(` Layer zone: ${a} (${(t.layer*100).toFixed(0)}%)`),n.push(` Openness: ${(t.openness*100).toFixed(0)}%`),n.push(` Description: ${t.description}`),n.push(` Last active: ${t.lastActive}`);const r=we(t.lastActive),d=t.weight!=null?ye(t.weight,t.lastActive):"stable";if(n.push(` Temporal: Last active ${r} | Weight trend: ${d}`),t.source){const c={seed:"seed data",agent:"observed from activity",manual:"filesystem scan",diary:"personal diary entry",conversation:"created from conversation","agent-monitor":"monitored agent activity"};n.push(` Source: ${c[t.source]??t.source}`)}if(t.weight!=null&&n.push(` Weight: ${t.weight.toFixed(2)}`),t.connections.length>0){const c=t.connections.map(i=>o.get(i)??ve(i));n.push(` Connections: ${c.join(", ")}`)}if(t.files&&t.files.length>0){const c=t.files.slice(0,10).map(i=>i.split("/").pop()??i);n.push(` Key files: ${c.join(", ")}`)}if(t.fileContents&&t.fileContents.length>0){n.push(" File contents:");for(const c of t.fileContents)n.push(` --- ${c.name} ---`),n.push(c.content.split(`
4
- `).map(i=>` ${i}`).join(`
2
+ import{runInitClaudeCode as de}from"./chunk-FWIISAZZ.js";import{AgentProfileManager as H,AlertManager as ge,AuditTrail as A,BaselineBuilder as N,CorrelationDetector as ue,DeviationDetector as B,FileStorageBackend as fe,ProfileStore as pe,ReportGenerator as me,Sentinel as T,SentinelRunner as he,generateFleetReport as ye}from"./chunk-GRN5P3H2.js";import{readReleaseToken as we}from"./chunk-LATQNIRW.js";import{deriveAgentId as $e}from"./chunk-B5QKJHSV.js";import"./chunk-FMZWHT4M.js";import"./chunk-QIYQWOLO.js";import{loadPolicy as ve}from"./chunk-WLIDSTS4.js";import{getOrCreateKeyPair as E}from"./chunk-NUXSUSYY.js";import{join as u}from"path";import{homedir as m}from"os";import{readFile as M,writeFile as R,access as K,mkdir as W}from"fs/promises";function Ae(e){const o=new Date(e);if(isNaN(o.getTime()))return"unknown";const t=Date.now()-o.getTime();if(t<0)return"just now";const a=Math.floor(t/6e4);if(a<1)return"just now";if(a<60)return`${a} minute${a===1?"":"s"} ago`;const r=Math.floor(a/60);if(r<24)return`${r} hour${r===1?"":"s"} ago`;const i=Math.floor(r/24);if(i<14)return`${i} day${i===1?"":"s"} ago`;const d=Math.floor(i/7);if(i<60)return`${d} week${d===1?"":"s"} ago`;if(i>=365)return"over a year ago";const c=Math.floor(i/30);return`${c} month${c===1?"":"s"} ago`}function Ie(e,o){const n=new Date(o),t=isNaN(n.getTime())?1/0:Math.floor((Date.now()-n.getTime())/864e5);return e<.3||t>30?"declining":e>.7&&t<7?"rising":"stable"}function Se(e){return e<.3?"inner":e<.65?"middle":"outer"}function G(e){if(e.length===0)return"No petals selected.";const o=new Map;for(const t of e)o.set(t.id,t.label);const n=[`Selected petals (${e.length}):
3
+ `];for(const t of e){const a=Se(t.layer),r=t.isRichData?"":" [filler]";n.push(`- ${t.label}${r}`),n.push(` Category: ${t.category}`),n.push(` Layer zone: ${a} (${(t.layer*100).toFixed(0)}%)`),n.push(` Openness: ${(t.openness*100).toFixed(0)}%`),n.push(` Description: ${t.description}`),n.push(` Last active: ${t.lastActive}`);const i=Ae(t.lastActive),d=t.weight!=null?Ie(t.weight,t.lastActive):"stable";if(n.push(` Temporal: Last active ${i} | Weight trend: ${d}`),t.source){const c={seed:"seed data",agent:"observed from activity",manual:"filesystem scan",diary:"personal diary entry",conversation:"created from conversation","agent-monitor":"monitored agent activity"};n.push(` Source: ${c[t.source]??t.source}`)}if(t.weight!=null&&n.push(` Weight: ${t.weight.toFixed(2)}`),t.connections.length>0){const c=t.connections.map(s=>o.get(s)??De(s));n.push(` Connections: ${c.join(", ")}`)}if(t.files&&t.files.length>0){const c=t.files.slice(0,10).map(s=>s.split("/").pop()??s);n.push(` Key files: ${c.join(", ")}`)}if(t.fileContents&&t.fileContents.length>0){n.push(" File contents:");for(const c of t.fileContents)n.push(` --- ${c.name} ---`),n.push(c.content.split(`
4
+ `).map(s=>` ${s}`).join(`
5
5
  `))}n.push("")}return n.join(`
6
- `)}function ve(e){return e.split("-").map(o=>o.charAt(0).toUpperCase()+o.slice(1)).join(" ")}function Ae(e){const o=[Ie(e.agentName,e.agentId,e.role),Se(e.baseline),De(e.e),Te(e.findings),Ce()],n=be(e.t);return n&&o.push(n),o.join(`
6
+ `)}function De(e){return e.split("-").map(o=>o.charAt(0).toUpperCase()+o.slice(1)).join(" ")}function be(e){const o=[Te(e.agentName,e.agentId,e.role),Re(e.baseline),Ce(e.e),Ee(e.findings),Me()],n=je(e.t);return n&&o.push(n),o.join(`
7
7
 
8
- `)}function Ie(e,o,n){const t=["=== Agent Identity ===",`Name: ${e}`,`ID: ${o}`];if(n){if(t.push(`Role: ${n.description}`),t.push(`Allowed actions: ${n.allowedActions.length>0?n.allowedActions.join(", "):"Not defined"}`),n.forbiddenTargetPatterns.length>0&&t.push(`Forbidden targets: ${n.forbiddenTargetPatterns.join(", ")}`),n.expectedSchedule){const a=[];n.expectedSchedule.activeHours&&a.push(`hours ${n.expectedSchedule.activeHours[0]}-${n.expectedSchedule.activeHours[1]}`),n.expectedSchedule.activeDays&&a.push(`days ${n.expectedSchedule.activeDays.join(", ")}`),a.length>0&&t.push(`Expected schedule: ${a.join("; ")}`)}}else t.push("No role definition \u2014 monitoring behavioral baseline only");return t.join(`
9
- `)}function Se(e){if(!e)return`=== Behavioral Baseline ===
8
+ `)}function Te(e,o,n){const t=["=== Agent Identity ===",`Name: ${e}`,`ID: ${o}`];if(n){if(t.push(`Role: ${n.description}`),t.push(`Allowed actions: ${n.allowedActions.length>0?n.allowedActions.join(", "):"Not defined"}`),n.forbiddenTargetPatterns.length>0&&t.push(`Forbidden targets: ${n.forbiddenTargetPatterns.join(", ")}`),n.expectedSchedule){const a=[];n.expectedSchedule.activeHours&&a.push(`hours ${n.expectedSchedule.activeHours[0]}-${n.expectedSchedule.activeHours[1]}`),n.expectedSchedule.activeDays&&a.push(`days ${n.expectedSchedule.activeDays.join(", ")}`),a.length>0&&t.push(`Expected schedule: ${a.join("; ")}`)}}else t.push("No role definition \u2014 monitoring behavioral baseline only");return t.join(`
9
+ `)}function Re(e){if(!e)return`=== Behavioral Baseline ===
10
10
  Not yet established. Insufficient data for baseline computation.`;const o=Object.entries(e.actionDistribution).map(([t,a])=>`${t}: ${a}%`).join(", ");return["=== Behavioral Baseline ===",`Period: ${e.periodDays} days`,`Total sessions: ${e.totalSessions}`,`Total events: ${e.totalEvents}`,`Average events per session: ${e.averageEventsPerSession}`,`Average session duration: ${e.averageSessionDurationMinutes} minutes`,`Typical active hours: ${e.typicalActiveHours[0]}-${e.typicalActiveHours[1]}`,`Typical active days: ${e.typicalActiveDays.length>0?e.typicalActiveDays.join(", "):"N/A"}`,`Action distribution: ${o||"N/A"}`,`Normal weight range: ${e.normalWeightRange[0].toFixed(2)}-${e.normalWeightRange[1].toFixed(2)}`,`Top targets: ${e.topTargets.length>0?e.topTargets.slice(0,10).join(", "):"N/A"}`].join(`
11
- `)}function De(e){const o=`=== Recent Activity (${e.length} sessions) ===`;return e.length===0?`${o}
11
+ `)}function Ce(e){const o=`=== Recent Activity (${e.length} sessions) ===`;return e.length===0?`${o}
12
12
  No recent activity recorded.`:`${o}
13
- ${U(e)}`}function Te(e){const o="=== Security Findings ===";if(e.length===0)return`${o}
13
+ ${G(e)}`}function Ee(e){const o="=== Security Findings ===";if(e.length===0)return`${o}
14
14
  No security findings detected. Agent behavior is within expected parameters.`;const n=[o];for(const t of e)n.push(`[${t.severity}] ${t.type}`),n.push(` Description: ${t.description}`),n.push(` Evidence: ${t.evidence.action} \u2192 ${t.evidence.target} at ${t.evidence.timestamp}`),t.evidence.baselineComparison&&n.push(` Baseline comparison: ${t.evidence.baselineComparison}`),n.push(` Recommendation: ${t.recommendation}`),n.push("");return n.join(`
15
- `)}function Ce(){return["=== Analysis Request ===","Based on the data above, provide:","1. OVERALL HEALTH ASSESSMENT: Is this agent operating normally?","2. RISK SUMMARY: What is the overall risk level?","3. FINDING REVIEW: For each finding, confirm or adjust its severity with reasoning.","4. RECOMMENDATIONS: Specific actions the owner should take.","5. MONITORING GUIDANCE: What patterns to watch for going forward."].join(`
16
- `)}function be(e){return!e||e.length===0?null:`=== Owner Context ===
17
- ${U(e)}`}var f=process.argv.slice(2),p=y(f,"--agent"),G=y(f,"--name"),Re=y(f,"--role"),Me=f.includes("--create"),Ee=f.includes("--compute-baseline"),je=f.includes("--monitor"),Ne=f.includes("--init-config"),ke=f.includes("--init-policy"),Fe=f.includes("init")&&f.includes("claude-code"),xe=f.includes("--force"),_=y(f,"--from-policy"),Pe=f.includes("--report"),Le=f.includes("--report-all"),Oe=f.includes("--correlations"),qe=f.includes("--verify-audit"),He=f.includes("--enroll-manifest"),Be=f.includes("--recompute-stats"),Ke=f.includes("--health"),We=f.includes("--restrict"),Ue=f.includes("--quarantine"),Ge=f.includes("--release"),_e=f.includes("--status"),Je=f.includes("--start-task"),Qe=f.includes("--end-task"),Ve=f.includes("--intent-status"),ze=f.includes("--intent-report"),J=y(f,"--task-id"),Q=y(f,"--description"),V=y(f,"--relaxed-actions"),z=y(f,"--phases"),Y=y(f,"--reason"),F=y(f,"--quarantine"),x=y(f,"--quarantine-reason"),j=y(f,"--period"),X=y(f,"--format"),S=new H;function y(e,o){const n=e.find(a=>a.startsWith(o+"="));if(!n)return;const t=n.indexOf("=");return n.slice(t+1)}async function T(e,o){try{const n=await E(u(e,o,"mode.json"),"utf-8"),t=JSON.parse(n);return t.mode==="restricted"||t.mode==="quarantined"||t.mode==="normal"?t:{mode:"normal"}}catch{return{mode:"normal"}}}function Z(e){const o=["Agent","Score","Status","Mode","C/H/M/L","Last Event","Baseline"],n=e.map(i=>[i.agentId,String(i.score),i.status,i.mode,i.findings,i.lastEvent,i.baseline?"\u2713":"\u2717"]),t=[o,...n],a=o.map((i,l)=>Math.max(...t.map(g=>g[l].length))),s=(i,l)=>i+" ".repeat(l-i.length),r=o.map((i,l)=>s(i,a[l])).join(" "),d=a.map(i=>"-".repeat(i)).join(" "),c=n.map(i=>i.map((l,g)=>s(l,a[g])).join(" "));return[r,d,...c].join(`
18
- `)}function ee(e){const o=["Agent ID","Mode","Reason","Changed"],n=e.map(i=>[i.agentId,i.mode.toUpperCase(),i.reason,i.changed]),t=[o,...n],a=o.map((i,l)=>Math.max(...t.map(g=>g[l].length))),s=(i,l)=>i+" ".repeat(l-i.length),r=o.map((i,l)=>s(i,a[l])).join(" "),d=a.map(i=>"-".repeat(i)).join(" "),c=n.map(i=>i.map((l,g)=>s(l,a[g])).join(" "));return[r,d,...c].join(`
19
- `)}function te(e){const o=["Agent ID","Sessions","Last Active","Role","Baseline","Mode","Status"],n=e.map(i=>[i.agentId,String(i.sessions),i.lastActive,i.roleDefined?"\u2713":"\u2717",i.baselineDefined?"\u2713":"\u2717",i.mode??"normal",i.status]),t=[o,...n],a=o.map((i,l)=>Math.max(...t.map(g=>g[l].length))),s=(i,l)=>i+" ".repeat(l-i.length),r=o.map((i,l)=>s(i,a[l])).join(" "),d=a.map(i=>"-".repeat(i)).join(" "),c=n.map(i=>i.map((l,g)=>s(l,a[g])).join(" "));return[r,d,...c].join(`
20
- `)}function P(e,o){return e<10?"New":o.some(n=>n.severity==="HIGH"||n.severity==="CRITICAL")?"At Risk":o.some(n=>n.severity==="MEDIUM")?"Caution":"Healthy"}function L(e,o){const n=Date.now()-o*24*60*60*1e3;return e.filter(t=>new Date(t.lastActive).getTime()>=n)}function ne(e){return{id:e.id,label:e.label,category:e.category,description:e.description,layer:e.layer,lastActive:e.lastActive,openness:e.openness,connections:e.connections,isRichData:!0,source:e.source,core:e.core,sharable:e.sharable,weight:e.weight,files:e.files}}function O(e,o){const n=["Sentinel \u2014 Live Monitoring","\u2500".repeat(26)];for(const[t,a]of e){const s=(o.get(t)??"manual").toUpperCase(),r=a.getFindings(),d=r.filter(w=>w.severity==="HIGH"||w.severity==="CRITICAL").length,c=r.filter(w=>w.severity==="MEDIUM").length;let i=`${r.length} findings`;d>0?i=`${d} HIGH`:c>0&&(i=`${c} MEDIUM`);const l=P(a.sessionCount,r),g=t.padEnd(18),h=s.padEnd(5);n.push(`[${g}] ${h}| ${a.eventCount} events | ${a.sessionCount} sessions | ${i} | ${l}`)}return n.push(""),n.push("Press Ctrl+C to stop."),n.join(`
21
- `)}function oe(e){const o=["Monitoring stopped. Summary:"];for(const[n,t]of e){const a=t.getFindings();let s=`${a.length} findings`;if(a.length>0){const r={};for(const c of a)r[c.severity]=(r[c.severity]??0)+1;const d=Object.entries(r).map(([c,i])=>`${i} ${c}`);s=`${a.length} finding${a.length>1?"s":""} (${d.join(", ")})`}o.push(`- ${n}: ${t.eventCount} events, ${t.sessionCount} sessions, ${s}`)}return o.join(`
22
- `)}async function Ye(e,o){const n=u(m(),".dahlia","agents"),t=await T(n,e);if(t.mode==="quarantined"){console.log(`Agent ${e} is quarantined \u2014 cannot downgrade to restricted.`);return}const a=t.mode;await W(u(n,e),{recursive:!0}),await b(u(n,e,"mode.json"),JSON.stringify({mode:"restricted",reason:o,timestamp:new Date().toISOString(),previousMode:a}),"utf-8");const s=await M(u(n,e)),r=new A(e,{logDir:u(n,e)});await r.open(),r.setSigningKey(s.privateKey,s.publicKey),await r.logModeChange("restricted",o,a),await r.close(),console.log(`Agent ${e} RESTRICTED: ${o}`)}async function Xe(e,o){const n=u(m(),".dahlia","agents"),a=(await T(n,e)).mode;await W(u(n,e),{recursive:!0}),await b(u(n,e,"mode.json"),JSON.stringify({mode:"quarantined",reason:o,timestamp:new Date().toISOString(),previousMode:a}),"utf-8");const s=await M(u(n,e)),r=new A(e,{logDir:u(n,e)});await r.open(),r.setSigningKey(s.privateKey,s.publicKey),await r.logModeChange("quarantined",o,a),await r.close(),console.log(`Agent ${e} QUARANTINED: ${o}`)}async function Ze(e){const o=u(m(),".dahlia","agents"),n=await T(o,e);if(n.mode==="normal"){console.log(`Agent ${e} is already in normal mode.`);return}const t=n.mode;await b(u(o,e,"mode.json"),JSON.stringify({mode:"normal",reason:"manual release",timestamp:new Date().toISOString(),previousMode:t}),"utf-8");const a=await M(u(o,e)),s=new A(e,{logDir:u(o,e)});await s.open(),s.setSigningKey(a.privateKey,a.publicKey),await s.logModeChange("normal","manual release",t),await s.close(),console.log(`Agent ${e} RELEASED (was ${t})`)}async function et(){const e=await S.listAgents();if(e.length===0){console.log("No monitored agents found.");return}const o=u(m(),".dahlia","agents"),n=[];for(const a of e){const s=await T(o,a);n.push({agentId:a,mode:s.mode,reason:s.reason??"-",changed:s.timestamp?s.timestamp.split("T")[0]:"-"})}console.log(`Agent Mode Status
23
- `),console.log(ee(n));const t=n.filter(a=>a.mode!=="normal");if(t.length>0){console.log("");for(const a of t){const s=a.mode==="quarantined"?"QUARANTINED":"RESTRICTED";console.log(`\u26A0 ${a.agentId}: ${s}`)}}}async function tt(){const e=await S.listAgents();if(e.length===0){console.log("No monitored agents found. Create one with --create --agent=ID --name=NAME");return}const o=[];for(const t of e)try{const r=(await S.loadAgentProfile(t)).build().filter(I=>I.source==="agent-monitor"),d=r.length,c=r.length>0?r.map(I=>I.lastActive).sort().reverse()[0].split("T")[0]:"never",l=await new k(t).loadBaseline(t),g=await S.loadRole(t),h=[];if(l){const I=L(r,7),N=new B(l,g);for(const $ of I){const D={label:$.label,category:$.category,lastActive:$.lastActive,description:$.description,weight:$.weight,source:$.source,files:$.files,connections:$.connections};h.push(...N.analyzeSession(D))}}const w=u(m(),".dahlia","agents"),R=await T(w,t);o.push({agentId:t,sessions:d,lastActive:c,roleDefined:g!==null,baselineDefined:l!==null,status:P(d,h),mode:R.mode})}catch(a){console.warn(`Error loading agent ${t}:`,a),o.push({agentId:t,sessions:0,lastActive:"error",roleDefined:!1,baselineDefined:!1,status:"Error",mode:"normal"})}const n=o.filter(t=>t.mode&&t.mode!=="normal");if(n.length>0){for(const t of n){const a=t.mode==="quarantined"?"QUARANTINED":"RESTRICTED";console.log(`\u26A0 ${t.agentId}: ${a}`)}console.log("")}console.log(`Sentinel Agent Overview
24
- `),console.log(te(o))}async function nt(e){const o=await S.loadAgentProfile(e),n=await S.loadRole(e),a=await new k(e).loadBaseline(e),r=o.build().filter(D=>D.source==="agent-monitor"),d=L(r,7),c=d.map(ne),i=[];if(a){const D=new B(a,n);for(const v of d){const re={label:v.label,category:v.category,lastActive:v.lastActive,description:v.description,weight:v.weight,source:v.source,files:v.files,connections:v.connections};i.push(...D.analyzeSession(re))}}const l=u(m(),".dahlia","profile.json"),g=new ge(l),h=new ue({backend:g});let w=[];await h.load()&&(w=h.build().filter(v=>v.core===!0).map(ne));const I=Ae({agentId:e,agentName:e,role:n,baseline:a,e:c,findings:i,t:w}),N=new Date().toISOString().split("T")[0],$=`Sentinel Agent Report \u2014 ${e} \u2014 ${d.length} recent sessions \u2014 generated ${N}
15
+ `)}function Me(){return["=== Analysis Request ===","Based on the data above, provide:","1. OVERALL HEALTH ASSESSMENT: Is this agent operating normally?","2. RISK SUMMARY: What is the overall risk level?","3. FINDING REVIEW: For each finding, confirm or adjust its severity with reasoning.","4. RECOMMENDATIONS: Specific actions the owner should take.","5. MONITORING GUIDANCE: What patterns to watch for going forward."].join(`
16
+ `)}function je(e){return!e||e.length===0?null:`=== Owner Context ===
17
+ ${G(e)}`}import{request as J}from"http";var f=process.argv.slice(2),p=w(f,"--agent"),U=w(f,"--name"),ke=w(f,"--role"),Ne=f.includes("--create"),Fe=f.includes("--compute-baseline"),xe=f.includes("--monitor"),Pe=f.includes("--init-config"),Oe=f.includes("--init-policy"),Le=f.includes("init")&&f.includes("claude-code"),qe=f.includes("--force"),_=w(f,"--from-policy"),He=f.includes("--report"),Be=f.includes("--report-all"),Ke=f.includes("--correlations"),We=f.includes("--verify-audit"),Ge=f.includes("--enroll-manifest"),Je=f.includes("--recompute-stats"),Ue=f.includes("--health"),_e=f.includes("--restrict"),Qe=f.includes("--quarantine"),Ve=f.includes("--release")||f[0]==="release",ze=f.includes("--status"),Ye=f.includes("--start-task"),Xe=f.includes("--end-task"),Ze=f.includes("--intent-status"),et=f.includes("--intent-report"),Q=w(f,"--task-id"),V=w(f,"--description"),z=w(f,"--relaxed-actions"),Y=w(f,"--phases"),X=w(f,"--reason"),F=w(f,"--quarantine"),x=w(f,"--quarantine-reason"),j=w(f,"--period"),Z=w(f,"--format"),S=new H;function w(e,o){const n=e.find(a=>a.startsWith(o+"="));if(!n)return;const t=n.indexOf("=");return n.slice(t+1)}async function b(e,o){try{const n=await M(u(e,o,"mode.json"),"utf-8"),t=JSON.parse(n);return t.mode==="restricted"||t.mode==="quarantined"||t.mode==="normal"?t:{mode:"normal"}}catch{return{mode:"normal"}}}function ee(e){const o=["Agent","Score","Status","Mode","C/H/M/L","Last Event","Baseline"],n=e.map(s=>[s.agentId,String(s.score),s.status,s.mode,s.findings,s.lastEvent,s.baseline?"\u2713":"\u2717"]),t=[o,...n],a=o.map((s,l)=>Math.max(...t.map(g=>g[l].length))),r=(s,l)=>s+" ".repeat(l-s.length),i=o.map((s,l)=>r(s,a[l])).join(" "),d=a.map(s=>"-".repeat(s)).join(" "),c=n.map(s=>s.map((l,g)=>r(l,a[g])).join(" "));return[i,d,...c].join(`
18
+ `)}function te(e){const o=["Agent ID","Mode","Reason","Changed"],n=e.map(s=>[s.agentId,s.mode.toUpperCase(),s.reason,s.changed]),t=[o,...n],a=o.map((s,l)=>Math.max(...t.map(g=>g[l].length))),r=(s,l)=>s+" ".repeat(l-s.length),i=o.map((s,l)=>r(s,a[l])).join(" "),d=a.map(s=>"-".repeat(s)).join(" "),c=n.map(s=>s.map((l,g)=>r(l,a[g])).join(" "));return[i,d,...c].join(`
19
+ `)}function ne(e){const o=["Agent ID","Sessions","Last Active","Role","Baseline","Mode","Status"],n=e.map(s=>[s.agentId,String(s.sessions),s.lastActive,s.roleDefined?"\u2713":"\u2717",s.baselineDefined?"\u2713":"\u2717",s.mode??"normal",s.status]),t=[o,...n],a=o.map((s,l)=>Math.max(...t.map(g=>g[l].length))),r=(s,l)=>s+" ".repeat(l-s.length),i=o.map((s,l)=>r(s,a[l])).join(" "),d=a.map(s=>"-".repeat(s)).join(" "),c=n.map(s=>s.map((l,g)=>r(l,a[g])).join(" "));return[i,d,...c].join(`
20
+ `)}function P(e,o){return e<10?"New":o.some(n=>n.severity==="HIGH"||n.severity==="CRITICAL")?"At Risk":o.some(n=>n.severity==="MEDIUM")?"Caution":"Healthy"}function O(e,o){const n=Date.now()-o*24*60*60*1e3;return e.filter(t=>new Date(t.lastActive).getTime()>=n)}function oe(e){return{id:e.id,label:e.label,category:e.category,description:e.description,layer:e.layer,lastActive:e.lastActive,openness:e.openness,connections:e.connections,isRichData:!0,source:e.source,core:e.core,sharable:e.sharable,weight:e.weight,files:e.files}}function L(e,o){const n=["Sentinel \u2014 Live Monitoring","\u2500".repeat(26)];for(const[t,a]of e){const r=(o.get(t)??"manual").toUpperCase(),i=a.getFindings(),d=i.filter(y=>y.severity==="HIGH"||y.severity==="CRITICAL").length,c=i.filter(y=>y.severity==="MEDIUM").length;let s=`${i.length} findings`;d>0?s=`${d} HIGH`:c>0&&(s=`${c} MEDIUM`);const l=P(a.sessionCount,i),g=t.padEnd(18),h=r.padEnd(5);n.push(`[${g}] ${h}| ${a.eventCount} events | ${a.sessionCount} sessions | ${s} | ${l}`)}return n.push(""),n.push("Press Ctrl+C to stop."),n.join(`
21
+ `)}function ae(e){const o=["Monitoring stopped. Summary:"];for(const[n,t]of e){const a=t.getFindings();let r=`${a.length} findings`;if(a.length>0){const i={};for(const c of a)i[c.severity]=(i[c.severity]??0)+1;const d=Object.entries(i).map(([c,s])=>`${s} ${c}`);r=`${a.length} finding${a.length>1?"s":""} (${d.join(", ")})`}o.push(`- ${n}: ${t.eventCount} events, ${t.sessionCount} sessions, ${r}`)}return o.join(`
22
+ `)}async function tt(e,o){const n=u(m(),".dahlia","agents"),t=await b(n,e);if(t.mode==="quarantined"){console.log(`Agent ${e} is quarantined \u2014 cannot downgrade to restricted.`);return}const a=t.mode;await W(u(n,e),{recursive:!0}),await R(u(n,e,"mode.json"),JSON.stringify({mode:"restricted",reason:o,timestamp:new Date().toISOString(),previousMode:a}),"utf-8");const r=await E(u(n,e)),i=new A(e,{logDir:u(n,e)});await i.open(),i.setSigningKey(r.privateKey,r.publicKey),await i.logModeChange("restricted",o,a),await i.close(),console.log(`Agent ${e} RESTRICTED: ${o}`)}async function nt(e,o){const n=u(m(),".dahlia","agents"),a=(await b(n,e)).mode;await W(u(n,e),{recursive:!0}),await R(u(n,e,"mode.json"),JSON.stringify({mode:"quarantined",reason:o,timestamp:new Date().toISOString(),previousMode:a}),"utf-8");const r=await E(u(n,e)),i=new A(e,{logDir:u(n,e)});await i.open(),i.setSigningKey(r.privateKey,r.publicKey),await i.logModeChange("quarantined",o,a),await i.close(),console.log(`Agent ${e} QUARANTINED: ${o}`)}function ot(e,o=1500){return new Promise(n=>{const t=J({host:"127.0.0.1",port:e,path:"/api/sentinel/health",method:"GET"},a=>{a.resume(),n(a.statusCode===200)});t.on("error",()=>n(!1)),t.setTimeout(o,()=>{t.destroy(),n(!1)}),t.end()})}function at(e,o,n){return new Promise((t,a)=>{const r=JSON.stringify({agentId:n,reason:"operator release (sentinel release)"}),i=J({host:"127.0.0.1",port:e,path:"/api/sentinel/release",method:"POST",headers:{"content-type":"application/json","content-length":Buffer.byteLength(r),"x-sentinel-token":o}},d=>{let c="";d.on("data",s=>c+=s),d.on("end",()=>{try{t({status:d.statusCode??0,body:JSON.parse(c)})}catch{t({status:d.statusCode??0,body:c})}})});i.on("error",a),i.setTimeout(3e3,()=>i.destroy(new Error("timeout"))),i.write(r),i.end()})}function se(e){const{id:o,selfDerived:n,previousMode:t,mode:a}=e;return n&&t==="normal"?`Agent ${o} was already normal \u2014 no change. If you are still locked out, your workspace id may differ (e.g. a symlinked root). Run: sentinel release --agent=<id>`:`[live] Agent ${o} RELEASED \u2014 was ${t}, now ${a}. Applied to the running daemon (no restart).`}async function st(e){const o=u(m(),".dahlia","agents"),n=e===void 0,t=e??$e(process.cwd());n&&console.log(`Resolved agent-id from cwd: ${t}`);const a=we(m());if(a&&await ot(a.port))try{const s=await at(a.port,a.token,t);if(s.status===200&&typeof s.body=="object"&&s.body.ok){console.log(se({id:t,selfDerived:n,previousMode:String(s.body.previousMode),mode:String(s.body.mode)}));return}const l=typeof s.body=="object"?JSON.stringify(s.body):s.body;console.error(`[live] daemon refused release (HTTP ${s.status}): ${l}`);return}catch(s){console.error(`[live] daemon release call failed: ${s.message}. Falling back to mode.json.`)}const r=await b(o,t);if(r.mode==="normal"){console.log(`Agent ${t} is already in normal mode.`);return}const i=r.mode;await R(u(o,t,"mode.json"),JSON.stringify({mode:"normal",reason:"manual release",timestamp:new Date().toISOString(),previousMode:i}),"utf-8");const d=await E(u(o,t)),c=new A(t,{logDir:u(o,t)});await c.open(),c.setSigningKey(d.privateKey,d.publicKey),await c.logModeChange("normal","manual release",i),await c.close(),console.log(`[fallback] Agent ${t} RELEASED (was ${i}) \u2014 no running daemon; mode.json written, applies on the daemon's next start.`)}async function it(){const e=await S.listAgents();if(e.length===0){console.log("No monitored agents found.");return}const o=u(m(),".dahlia","agents"),n=[];for(const a of e){const r=await b(o,a);n.push({agentId:a,mode:r.mode,reason:r.reason??"-",changed:r.timestamp?r.timestamp.split("T")[0]:"-"})}console.log(`Agent Mode Status
23
+ `),console.log(te(n));const t=n.filter(a=>a.mode!=="normal");if(t.length>0){console.log("");for(const a of t){const r=a.mode==="quarantined"?"QUARANTINED":"RESTRICTED";console.log(`\u26A0 ${a.agentId}: ${r}`)}}}async function rt(){const e=await S.listAgents();if(e.length===0){console.log("No monitored agents found. Create one with --create --agent=ID --name=NAME");return}const o=[];for(const t of e)try{const i=(await S.loadAgentProfile(t)).build().filter(I=>I.source==="agent-monitor"),d=i.length,c=i.length>0?i.map(I=>I.lastActive).sort().reverse()[0].split("T")[0]:"never",l=await new N(t).loadBaseline(t),g=await S.loadRole(t),h=[];if(l){const I=O(i,7),k=new B(l,g);for(const $ of I){const D={label:$.label,category:$.category,lastActive:$.lastActive,description:$.description,weight:$.weight,source:$.source,files:$.files,connections:$.connections};h.push(...k.analyzeSession(D))}}const y=u(m(),".dahlia","agents"),C=await b(y,t);o.push({agentId:t,sessions:d,lastActive:c,roleDefined:g!==null,baselineDefined:l!==null,status:P(d,h),mode:C.mode})}catch(a){console.warn(`Error loading agent ${t}:`,a),o.push({agentId:t,sessions:0,lastActive:"error",roleDefined:!1,baselineDefined:!1,status:"Error",mode:"normal"})}const n=o.filter(t=>t.mode&&t.mode!=="normal");if(n.length>0){for(const t of n){const a=t.mode==="quarantined"?"QUARANTINED":"RESTRICTED";console.log(`\u26A0 ${t.agentId}: ${a}`)}console.log("")}console.log(`Sentinel Agent Overview
24
+ `),console.log(ne(o))}async function lt(e){const o=await S.loadAgentProfile(e),n=await S.loadRole(e),a=await new N(e).loadBaseline(e),i=o.build().filter(D=>D.source==="agent-monitor"),d=O(i,7),c=d.map(oe),s=[];if(a){const D=new B(a,n);for(const v of d){const ce={label:v.label,category:v.category,lastActive:v.lastActive,description:v.description,weight:v.weight,source:v.source,files:v.files,connections:v.connections};s.push(...D.analyzeSession(ce))}}const l=u(m(),".dahlia","profile.json"),g=new fe(l),h=new pe({backend:g});let y=[];await h.load()&&(y=h.build().filter(v=>v.core===!0).map(oe));const I=be({agentId:e,agentName:e,role:n,baseline:a,e:c,findings:s,t:y}),k=new Date().toISOString().split("T")[0],$=`Sentinel Agent Report \u2014 ${e} \u2014 ${d.length} recent sessions \u2014 generated ${k}
25
25
 
26
- `;process.stdout.write($+I)}async function ot(e){const o=u(m(),".dahlia","agents"),n=new A(e,{logDir:u(o,e)});await n.open();const t=new k(e),a=await t.computeBaseline(n);await n.close(),await t.saveBaseline(a),console.log(`Baseline computed for agent: ${e}`),console.log(` Sessions: ${a.totalSessions}`),console.log(` Period: ${a.periodDays} days`),console.log(" Action distribution:");for(const[s,r]of Object.entries(a.actionDistribution))console.log(` ${s}: ${r}%`);console.log(` Saved to: ~/.dahlia/agents/${e}/baseline.json`)}async function at(e,o,n){let t;if(n){let a;try{a=await E(n,"utf-8")}catch(s){console.error(`Failed to read role file "${n}":`,s);return}try{t=JSON.parse(a)}catch(s){console.error(`Invalid JSON in role file "${n}":`,s.message);return}}await S.createAgent(e,o,t),console.log(`Agent created: ${e} (${o})`),t&&console.log(` Role loaded from: ${n}`)}async function st(){const e=u(m(),".dahlia","sentinel.json");try{await K(e),console.log(`Config already exists at ${e}`);return}catch{}await b(e,JSON.stringify({agents:[{agentId:"example-agent",name:"Example Agent",adapterType:"log",logPath:"/path/to/agent/activity.log",logFormat:"json-lines",roleDefinitionPath:"~/.dahlia/agents/example-agent/role.json"}],baselineWindowDays:30},null,2)+`
27
- `),console.log("Template config created at ~/.dahlia/sentinel.json \u2014 edit it with your agent details then run: npm run sentinel -- --monitor")}async function q(){const e=u(m(),".dahlia","sentinel.json");try{const o=await E(e,"utf-8");return JSON.parse(o)}catch(o){if(o.code==="ENOENT")return null;if(o instanceof SyntaxError)return console.error(`Invalid JSON in sentinel config: ${o.message}`),null;throw o}}async function it(e){const o=await q();if(!o){console.log("No sentinel config found. Create ~/.dahlia/sentinel.json or use --init-config to add agents. See docs for config format.");return}let n=o.agents;if(e&&(n=n.filter(l=>l.agentId===e),n.length===0)){console.log(`Agent "${e}" not found in sentinel.json`);return}const t=new Map,a=new Map,s=l=>l&&l.startsWith("~/")?m()+l.slice(1):l;let r;if(o.alerts){const l={...o.alerts.minSeverity&&{minSeverity:o.alerts.minSeverity},...o.alerts.dedupeWindowMs!==void 0&&{dedupeWindowMs:o.alerts.dedupeWindowMs},...o.alerts.quietHoursEnabled!==void 0&&{quietHoursEnabled:o.alerts.quietHoursEnabled},...o.alerts.quietHours&&{quietHours:o.alerts.quietHours},...o.alerts.channels&&{channels:o.alerts.channels}};r=new ce(l)}for(const l of n)try{const g=new pe(l.agentId,void 0,{type:l.adapterType,logPath:s(l.logPath),logFormat:l.logFormat,fieldMapping:l.fieldMapping,mcpLogDir:s(l.mcpLogDir),webhookPort:l.webhookPort,webhookApiKey:l.webhookApiKey,readExisting:l.readExisting});g.setAuditLogDir(u(m(),".dahlia","agents",l.agentId)),g.setFindingCallback(h=>{(h.severity==="HIGH"||h.severity==="CRITICAL")&&console.log(`
28
- \u26A0 [${h.agentId}] ${h.severity}: ${h.description}`)}),r&&g.setAlertManager(r),await g.start(),t.set(l.agentId,g),a.set(l.agentId,l.adapterType)}catch(g){console.error(`Failed to start monitoring for agent ${l.agentId}:`,g)}console.log(O(t,a));const d=setInterval(()=>{console.clear(),console.log(O(t,a))},5e3),c=async()=>{clearInterval(d);for(const[l,g]of t)try{await g.stop()}catch(h){console.warn(`Error stopping runner ${l}:`,h)}console.log(`
29
- `+oe(t)),process.exit(0)},i=()=>{c().catch(l=>{console.error("Error during shutdown:",l),process.exit(1)})};process.on("SIGINT",i),process.on("SIGTERM",i)}async function rt(e,o,n){const a=await new fe(e).generateReport({periodDays:o,format:n});process.stdout.write(a)}async function lt(e,o){const n=await he({periodDays:e,format:o});process.stdout.write(n)}async function ct(){const e=await q();if(!e){console.log("No sentinel config found. Create ~/.dahlia/sentinel.json or use --init-config.");return}const o=u(m(),".dahlia","agents"),n=new Map;try{for(const s of e.agents){const r=new A(s.agentId,{logDir:u(o,s.agentId)});await r.open(),n.set(s.agentId,r)}const a=await new de().detect(n);if(a.length===0){console.log("No cross-agent correlations detected.");return}console.log(`Found ${a.length} cross-agent correlation(s):
30
- `);for(const s of a)console.log(`[${s.severity}] ${s.rule}`),console.log(` Agent A: ${s.agentA.agentId} \u2014 ${s.agentA.action} on ${s.agentA.target}`),console.log(` Agent B: ${s.agentB.agentId} \u2014 ${s.agentB.action} on ${s.agentB.target}`),console.log(` Time delta: ${Math.round(s.timeDeltaMs/1e3)}s`),console.log(` Recommendation: ${s.recommendation}`),console.log("")}finally{for(const t of n.values())await t.close()}}async function dt(e){const o=u(m(),".dahlia","agents"),n=u(o,e,"cumulative-stats.json");let t="(none)";try{t=JSON.parse(await E(n,"utf-8")).totalEntries}catch{}const s=await new A(e,{logDir:u(o,e)}).recomputeCumulativeStats();console.log(`Recomputed cumulative-stats for ${e}:`),console.log(` totalEntries: ${t} \u2192 ${s.totalEntries}`),console.log(` scope: ${s.countScope}`),console.log(" (Read-only over the signed trail; only cumulative-stats.json rewritten.)")}async function gt(e){const o=u(m(),".dahlia","agents"),n=u(o,e),t=await M(n),a=new A(e,{logDir:n});a.setSigningKey(t.privateKey,t.publicKey);const s=F&&x?[{file:F,reason:x}]:void 0,r=await a.enrollManifest(s?{quarantine:s}:void 0);console.log(`Manifest enrolled for ${e}: ${r.records} file record(s) signed + chained`+(r.quarantined?`, ${r.quarantined} quarantine record(s)`:"")+"."),s&&console.log(` Quarantined: ${F} \u2014 ${x}`),console.log(" (Read-only over audit entries; protects from enrollment forward.)")}async function ut(e){const o=u(m(),".dahlia","agents"),n=new A(e,{logDir:u(o,e)});await n.open();const t=await n.verify();if(t.valid?console.log(`Audit trail for ${e}: VALID (${t.totalEntries} entries verified)`):t.brokenAt!==void 0?console.log(`Audit trail for ${e}: BROKEN at entry ${t.brokenAt} (${t.totalEntries} entries checked)`):console.log(`Audit trail for ${e}: INVALID \u2014 file-level manifest integrity failure (${t.totalEntries} entries chain-verified)`),t.manifest){const s=t.manifest;console.log(` Manifest: ${s.recordCount} file record(s) \u2014 ${s.ok?"OK":`${s.issues.length} issue(s)`}`);for(const r of s.issues)console.log(` [${r.type}] ${r.detail}`);for(const r of s.quarantined)console.log(` [QUARANTINED] ${r.file} \u2014 ${r.reason} (retained on disk, excluded from verdict)`)}const a=await n.query({type:"finding",limit:1e4});if(a.length>0){let s=0,r=0,d=0;for(const i of a){const l=i.decision;l==="deny"?r++:l==="modify"?d++:s++}console.log(` Findings: ${a.length} total \u2014 ${s} allowed, ${r} denied, ${d} modified`);const c=a.slice(0,5);for(const i of c){const l=i,g=l.decision,h=l.modification,w=l.description,R=l._decisionDefaulted===!0;g==="modify"&&h?.type==="append_args"?console.log(` [modified] ${w} \u2014 appended args: [${h.args.join(", ")}]`):console.log(g==="deny"?` [denied] ${w}`:R?` [allowed (legacy)] ${w}`:` [allowed] ${w}`)}}await n.close()}async function ft(){const e=u(m(),".dahlia","agents"),o=new C({agentsDir:e}),t=await new H(e).listAgents();if(t.length===0){console.log("No monitored agents found."),await o.stop();return}const a=[];for(const s of t){const r=await o.getHealthScore(s);a.push({agentId:s,score:r.score,status:r.status,mode:r.mode,findings:`${r.findings.critical}/${r.findings.high}/${r.findings.medium}/${r.findings.low}`,lastEvent:r.lastEvent?r.lastEvent.split("T")[0]:"-",baseline:r.baselineEstablished})}await o.stop(),console.log(`Agent Health Dashboard
31
- `),console.log(Z(a))}var ae=`# Sentinel \u2014 Agent Security Policy
26
+ `;process.stdout.write($+I)}async function ct(e){const o=u(m(),".dahlia","agents"),n=new A(e,{logDir:u(o,e)});await n.open();const t=new N(e),a=await t.computeBaseline(n);await n.close(),await t.saveBaseline(a),console.log(`Baseline computed for agent: ${e}`),console.log(` Sessions: ${a.totalSessions}`),console.log(` Period: ${a.periodDays} days`),console.log(" Action distribution:");for(const[r,i]of Object.entries(a.actionDistribution))console.log(` ${r}: ${i}%`);console.log(` Saved to: ~/.dahlia/agents/${e}/baseline.json`)}async function dt(e,o,n){let t;if(n){let a;try{a=await M(n,"utf-8")}catch(r){console.error(`Failed to read role file "${n}":`,r);return}try{t=JSON.parse(a)}catch(r){console.error(`Invalid JSON in role file "${n}":`,r.message);return}}await S.createAgent(e,o,t),console.log(`Agent created: ${e} (${o})`),t&&console.log(` Role loaded from: ${n}`)}async function gt(){const e=u(m(),".dahlia","sentinel.json");try{await K(e),console.log(`Config already exists at ${e}`);return}catch{}await R(e,JSON.stringify({agents:[{agentId:"example-agent",name:"Example Agent",adapterType:"log",logPath:"/path/to/agent/activity.log",logFormat:"json-lines",roleDefinitionPath:"~/.dahlia/agents/example-agent/role.json"}],baselineWindowDays:30},null,2)+`
27
+ `),console.log("Template config created at ~/.dahlia/sentinel.json \u2014 edit it with your agent details then run: npm run sentinel -- --monitor")}async function q(){const e=u(m(),".dahlia","sentinel.json");try{const o=await M(e,"utf-8");return JSON.parse(o)}catch(o){if(o.code==="ENOENT")return null;if(o instanceof SyntaxError)return console.error(`Invalid JSON in sentinel config: ${o.message}`),null;throw o}}async function ut(e){const o=await q();if(!o){console.log("No sentinel config found. Create ~/.dahlia/sentinel.json or use --init-config to add agents. See docs for config format.");return}let n=o.agents;if(e&&(n=n.filter(l=>l.agentId===e),n.length===0)){console.log(`Agent "${e}" not found in sentinel.json`);return}const t=new Map,a=new Map,r=l=>l&&l.startsWith("~/")?m()+l.slice(1):l;let i;if(o.alerts){const l={...o.alerts.minSeverity&&{minSeverity:o.alerts.minSeverity},...o.alerts.dedupeWindowMs!==void 0&&{dedupeWindowMs:o.alerts.dedupeWindowMs},...o.alerts.quietHoursEnabled!==void 0&&{quietHoursEnabled:o.alerts.quietHoursEnabled},...o.alerts.quietHours&&{quietHours:o.alerts.quietHours},...o.alerts.channels&&{channels:o.alerts.channels}};i=new ge(l)}for(const l of n)try{const g=new he(l.agentId,void 0,{type:l.adapterType,logPath:r(l.logPath),logFormat:l.logFormat,fieldMapping:l.fieldMapping,mcpLogDir:r(l.mcpLogDir),webhookPort:l.webhookPort,webhookApiKey:l.webhookApiKey,readExisting:l.readExisting});g.setAuditLogDir(u(m(),".dahlia","agents",l.agentId)),g.setFindingCallback(h=>{(h.severity==="HIGH"||h.severity==="CRITICAL")&&console.log(`
28
+ \u26A0 [${h.agentId}] ${h.severity}: ${h.description}`)}),i&&g.setAlertManager(i),await g.start(),t.set(l.agentId,g),a.set(l.agentId,l.adapterType)}catch(g){console.error(`Failed to start monitoring for agent ${l.agentId}:`,g)}console.log(L(t,a));const d=setInterval(()=>{console.clear(),console.log(L(t,a))},5e3),c=async()=>{clearInterval(d);for(const[l,g]of t)try{await g.stop()}catch(h){console.warn(`Error stopping runner ${l}:`,h)}console.log(`
29
+ `+ae(t)),process.exit(0)},s=()=>{c().catch(l=>{console.error("Error during shutdown:",l),process.exit(1)})};process.on("SIGINT",s),process.on("SIGTERM",s)}async function ft(e,o,n){const a=await new me(e).generateReport({periodDays:o,format:n});process.stdout.write(a)}async function pt(e,o){const n=await ye({periodDays:e,format:o});process.stdout.write(n)}async function mt(){const e=await q();if(!e){console.log("No sentinel config found. Create ~/.dahlia/sentinel.json or use --init-config.");return}const o=u(m(),".dahlia","agents"),n=new Map;try{for(const r of e.agents){const i=new A(r.agentId,{logDir:u(o,r.agentId)});await i.open(),n.set(r.agentId,i)}const a=await new ue().detect(n);if(a.length===0){console.log("No cross-agent correlations detected.");return}console.log(`Found ${a.length} cross-agent correlation(s):
30
+ `);for(const r of a)console.log(`[${r.severity}] ${r.rule}`),console.log(` Agent A: ${r.agentA.agentId} \u2014 ${r.agentA.action} on ${r.agentA.target}`),console.log(` Agent B: ${r.agentB.agentId} \u2014 ${r.agentB.action} on ${r.agentB.target}`),console.log(` Time delta: ${Math.round(r.timeDeltaMs/1e3)}s`),console.log(` Recommendation: ${r.recommendation}`),console.log("")}finally{for(const t of n.values())await t.close()}}async function ht(e){const o=u(m(),".dahlia","agents"),n=u(o,e,"cumulative-stats.json");let t="(none)";try{t=JSON.parse(await M(n,"utf-8")).totalEntries}catch{}const r=await new A(e,{logDir:u(o,e)}).recomputeCumulativeStats();console.log(`Recomputed cumulative-stats for ${e}:`),console.log(` totalEntries: ${t} \u2192 ${r.totalEntries}`),console.log(` scope: ${r.countScope}`),console.log(" (Read-only over the signed trail; only cumulative-stats.json rewritten.)")}async function yt(e){const o=u(m(),".dahlia","agents"),n=u(o,e),t=await E(n),a=new A(e,{logDir:n});a.setSigningKey(t.privateKey,t.publicKey);const r=F&&x?[{file:F,reason:x}]:void 0,i=await a.enrollManifest(r?{quarantine:r}:void 0);console.log(`Manifest enrolled for ${e}: ${i.records} file record(s) signed + chained`+(i.quarantined?`, ${i.quarantined} quarantine record(s)`:"")+"."),r&&console.log(` Quarantined: ${F} \u2014 ${x}`),console.log(" (Read-only over audit entries; protects from enrollment forward.)")}async function wt(e){const o=u(m(),".dahlia","agents"),n=new A(e,{logDir:u(o,e)});await n.open();const t=await n.verify();if(t.valid?console.log(`Audit trail for ${e}: VALID (${t.totalEntries} entries verified)`):t.brokenAt!==void 0?console.log(`Audit trail for ${e}: BROKEN at entry ${t.brokenAt} (${t.totalEntries} entries checked)`):console.log(`Audit trail for ${e}: INVALID \u2014 file-level manifest integrity failure (${t.totalEntries} entries chain-verified)`),t.manifest){const r=t.manifest;console.log(` Manifest: ${r.recordCount} file record(s) \u2014 ${r.ok?"OK":`${r.issues.length} issue(s)`}`);for(const i of r.issues)console.log(` [${i.type}] ${i.detail}`);for(const i of r.quarantined)console.log(` [QUARANTINED] ${i.file} \u2014 ${i.reason} (retained on disk, excluded from verdict)`)}const a=await n.query({type:"finding",limit:1e4});if(a.length>0){let r=0,i=0,d=0;for(const s of a){const l=s.decision;l==="deny"?i++:l==="modify"?d++:r++}console.log(` Findings: ${a.length} total \u2014 ${r} allowed, ${i} denied, ${d} modified`);const c=a.slice(0,5);for(const s of c){const l=s,g=l.decision,h=l.modification,y=l.description,C=l._decisionDefaulted===!0;g==="modify"&&h?.type==="append_args"?console.log(` [modified] ${y} \u2014 appended args: [${h.args.join(", ")}]`):console.log(g==="deny"?` [denied] ${y}`:C?` [allowed (legacy)] ${y}`:` [allowed] ${y}`)}}await n.close()}async function $t(){const e=u(m(),".dahlia","agents"),o=new T({agentsDir:e}),t=await new H(e).listAgents();if(t.length===0){console.log("No monitored agents found."),await o.stop();return}const a=[];for(const r of t){const i=await o.getHealthScore(r);a.push({agentId:r,score:i.score,status:i.status,mode:i.mode,findings:`${i.findings.critical}/${i.findings.high}/${i.findings.medium}/${i.findings.low}`,lastEvent:i.lastEvent?i.lastEvent.split("T")[0]:"-",baseline:i.baselineEstablished})}await o.stop(),console.log(`Agent Health Dashboard
31
+ `),console.log(ee(a))}var ie=`# Sentinel \u2014 Agent Security Policy
32
32
  version: "1.0"
33
33
 
34
34
  agent:
@@ -72,9 +72,9 @@ policy:
72
72
  # repoRoot: . # scan this directory for sensitive files
73
73
  # mapPath: ~/.dahlia/repo-sensitivity.json
74
74
  # overlayPath: ~/.dahlia/repo-sensitivity.review.json
75
- `;async function se(){const e=u(process.cwd(),".sentinel.yaml");try{await K(e),console.log(`.sentinel.yaml already exists in ${process.cwd()}`);return}catch{}await b(e,ae,"utf-8"),console.log("Created .sentinel.yaml \u2014 edit then run: npm run sentinel -- --from-policy .sentinel.yaml")}async function ie(e){const o=await me(e),n=await C.fromPolicy(e);console.log(`Loaded policy for agent ${o.agent.id}. Monitoring active.`);const t=async()=>{await n.stop(),console.log("Monitoring stopped."),process.exit(0)},a=()=>{t().catch(s=>{console.error("Error during shutdown:",s),process.exit(1)})};process.on("SIGINT",a),process.on("SIGTERM",a)}async function pt(e,o,n){const t=u(m(),".dahlia","agents"),a=new C({agentsDir:t});await a.addAgent(e,e);const s=V?V.split(","):void 0,r=z?z.split(",").map(c=>c.trim()):void 0,d=a.startTask(e,o,n,{relaxedActions:s,phases:r});if(!d){console.log(`Failed to start task for agent ${e}.`),await a.stop();return}console.log(`Task started for agent ${e}:`),console.log(` Task ID: ${d.taskId}`),console.log(` Description: ${d.description}`),console.log(` Keywords: ${d.keywords.join(", ")}`),d.relaxedActions?.length&&console.log(` Relaxed: ${d.relaxedActions.join(", ")}`),d.phases?.length&&console.log(` Phases: ${d.phases.join(", ")}`),console.log(` TTL: ${(d.ttlMs/6e4).toFixed(0)} minutes`),await a.stop()}async function ht(e){const o=u(m(),".dahlia","agents"),n=new C({agentsDir:o});await n.addAgent(e,e);const t=n.endTask(e);t?(console.log(`Task ended for agent ${e}:`),console.log(` Task ID: ${t.taskId}`),console.log(` Description: ${t.description}`),console.log(` Status: ${t.status}`)):console.log(`No active task found for agent ${e}.`),await n.stop()}async function mt(e){const o=u(m(),".dahlia","agents"),n=new C({agentsDir:o});await n.addAgent(e,e);const t=n.getActiveTask(e);if(!t){console.log(`No active task for agent ${e}.`),await n.stop();return}const a=Date.now()-new Date(t.startedAt).getTime(),s=t.ttlMs-a;console.log(`Active task for agent ${e}:
76
- `),console.log(` Task ID: ${t.taskId}`),console.log(` Description: ${t.description}`),console.log(` Keywords: ${t.keywords.join(", ")}`),console.log(` Status: ${t.status}`),console.log(` Active for: ${(a/6e4).toFixed(1)} minutes`),console.log(` TTL remain: ${s>0?(s/6e4).toFixed(1)+" minutes":"EXPIRED"}`),t.relaxedActions?.length&&console.log(` Relaxed: ${t.relaxedActions.join(", ")}`),t.phases?.length&&console.log(` Phases: ${t.phases.join(", ")}`),t.acceptableActions.length>0&&console.log(` Acceptable: ${t.acceptableActions.map(r=>`${r.action}:${r.targetPattern}`).join(", ")}`),await n.stop()}async function wt(e){const o=u(m(),".dahlia","agents"),n=new A(e,{logDir:u(o,e)});await n.open();const t=await n.getStats(),a=await n.query({type:"intent_check"}),r=(await n.query({type:"finding"})).filter(c=>typeof c.data=="object"&&c.data!==null&&"type"in c.data&&c.data.type==="intent_drift");console.log(`Intent Alignment Report \u2014 ${e}
77
- `),console.log(` Intent starts: ${t.intentStartCount}`),console.log(` Intent ends: ${t.intentEndCount}`),console.log(` Intent checks: ${t.intentCheckCount}`),console.log(` Drift findings: ${t.intentDriftCount}`),t.averageAlignmentScore!==null&&console.log(` Avg alignment: ${t.averageAlignmentScore.toFixed(2)}`);const d=a.filter(c=>{const i=c.data;return i&&typeof i=="object"&&"aligned"in i&&!i.aligned}).slice(-5);if(d.length>0){console.log(`
78
- Last ${d.length} misaligned action(s):`);for(const c of d){const i=c.data,l=typeof i.score=="number"?i.score.toFixed(2):"?",g=i.event,h=g?.action??"?",w=g?.primaryTarget??g?.targets?.[0]??g?.target??"?";console.log(` score: ${l} ${h} \u2192 ${w} (${c.timestamp.split("T")[0]})`)}}if(r.length>0){console.log(`
79
- Recent drift findings: ${r.length}`);for(const c of r.slice(-3)){const i=c.data,l=i.severity??"?",g=i.description??"";console.log(` [${l}] ${typeof g=="string"?g.slice(0,80):g}`)}}await n.close()}async function yt(e,o){try{const n=await le({force:e,port:o});if(n.created.length>0){console.log("Created:");for(const t of n.created)console.log(` ${t}`)}if(n.skipped.length>0){console.log("Skipped (already exists \u2014 use --force to overwrite):");for(const t of n.skipped)console.log(` ${t}`)}if(n.merged.length>0){console.log("Merged:");for(const t of n.merged)console.log(` ${t}`)}if(n.errors.length>0){console.log("Errors:");for(const t of n.errors)console.log(` ${t}`)}n.errors.length===0&&console.log(`
80
- Sentinel + Claude Code integration ready.`)}catch(n){console.error(`Init failed: ${n.message}`),process.exit(1)}}if(Fe){const e=y(f,"--port");await yt(xe,e?parseInt(e,10):void 0)}else if(Je&&p&&J&&Q)await pt(p,J,Q);else if(Qe&&p)await ht(p);else if(Ve&&p)await mt(p);else if(ze&&p)await wt(p);else if(ke)await se();else if(_)await ie(_);else if(He&&p)await gt(p);else if(Be&&p)await dt(p);else if(qe&&p)await ut(p);else if(Ke)await ft();else if(We&&p)await Ye(p,Y??"No reason provided");else if(Ue&&p)await Xe(p,Y??"No reason provided");else if(Ge&&p)await Ze(p);else if(_e)await et();else if(Ne)await st();else if(je)await it(p);else if(Me&&p&&G)await at(p,G,Re);else if(Ee&&p)await ot(p);else if(Le){const e=j?parseInt(j,10):30;await lt(e,X==="json"?"json":"markdown")}else if(Pe&&p){const e=j?parseInt(j,10):30;await rt(p,e,X==="json"?"json":"markdown")}else Oe?await ct():p?await nt(p):await tt();
75
+ `;async function re(){const e=u(process.cwd(),".sentinel.yaml");try{await K(e),console.log(`.sentinel.yaml already exists in ${process.cwd()}`);return}catch{}await R(e,ie,"utf-8"),console.log("Created .sentinel.yaml \u2014 edit then run: npm run sentinel -- --from-policy .sentinel.yaml")}async function le(e){const o=await ve(e),n=await T.fromPolicy(e);console.log(`Loaded policy for agent ${o.agent.id}. Monitoring active.`);const t=async()=>{await n.stop(),console.log("Monitoring stopped."),process.exit(0)},a=()=>{t().catch(r=>{console.error("Error during shutdown:",r),process.exit(1)})};process.on("SIGINT",a),process.on("SIGTERM",a)}async function vt(e,o,n){const t=u(m(),".dahlia","agents"),a=new T({agentsDir:t});await a.addAgent(e,e);const r=z?z.split(","):void 0,i=Y?Y.split(",").map(c=>c.trim()):void 0,d=a.startTask(e,o,n,{relaxedActions:r,phases:i});if(!d){console.log(`Failed to start task for agent ${e}.`),await a.stop();return}console.log(`Task started for agent ${e}:`),console.log(` Task ID: ${d.taskId}`),console.log(` Description: ${d.description}`),console.log(` Keywords: ${d.keywords.join(", ")}`),d.relaxedActions?.length&&console.log(` Relaxed: ${d.relaxedActions.join(", ")}`),d.phases?.length&&console.log(` Phases: ${d.phases.join(", ")}`),console.log(` TTL: ${(d.ttlMs/6e4).toFixed(0)} minutes`),await a.stop()}async function At(e){const o=u(m(),".dahlia","agents"),n=new T({agentsDir:o});await n.addAgent(e,e);const t=n.endTask(e);t?(console.log(`Task ended for agent ${e}:`),console.log(` Task ID: ${t.taskId}`),console.log(` Description: ${t.description}`),console.log(` Status: ${t.status}`)):console.log(`No active task found for agent ${e}.`),await n.stop()}async function It(e){const o=u(m(),".dahlia","agents"),n=new T({agentsDir:o});await n.addAgent(e,e);const t=n.getActiveTask(e);if(!t){console.log(`No active task for agent ${e}.`),await n.stop();return}const a=Date.now()-new Date(t.startedAt).getTime(),r=t.ttlMs-a;console.log(`Active task for agent ${e}:
76
+ `),console.log(` Task ID: ${t.taskId}`),console.log(` Description: ${t.description}`),console.log(` Keywords: ${t.keywords.join(", ")}`),console.log(` Status: ${t.status}`),console.log(` Active for: ${(a/6e4).toFixed(1)} minutes`),console.log(` TTL remain: ${r>0?(r/6e4).toFixed(1)+" minutes":"EXPIRED"}`),t.relaxedActions?.length&&console.log(` Relaxed: ${t.relaxedActions.join(", ")}`),t.phases?.length&&console.log(` Phases: ${t.phases.join(", ")}`),t.acceptableActions.length>0&&console.log(` Acceptable: ${t.acceptableActions.map(i=>`${i.action}:${i.targetPattern}`).join(", ")}`),await n.stop()}async function St(e){const o=u(m(),".dahlia","agents"),n=new A(e,{logDir:u(o,e)});await n.open();const t=await n.getStats(),a=await n.query({type:"intent_check"}),i=(await n.query({type:"finding"})).filter(c=>typeof c.data=="object"&&c.data!==null&&"type"in c.data&&c.data.type==="intent_drift");console.log(`Intent Alignment Report \u2014 ${e}
77
+ `),console.log(` Intent starts: ${t.intentStartCount}`),console.log(` Intent ends: ${t.intentEndCount}`),console.log(` Intent checks: ${t.intentCheckCount}`),console.log(` Drift findings: ${t.intentDriftCount}`),t.averageAlignmentScore!==null&&console.log(` Avg alignment: ${t.averageAlignmentScore.toFixed(2)}`);const d=a.filter(c=>{const s=c.data;return s&&typeof s=="object"&&"aligned"in s&&!s.aligned}).slice(-5);if(d.length>0){console.log(`
78
+ Last ${d.length} misaligned action(s):`);for(const c of d){const s=c.data,l=typeof s.score=="number"?s.score.toFixed(2):"?",g=s.event,h=g?.action??"?",y=g?.primaryTarget??g?.targets?.[0]??g?.target??"?";console.log(` score: ${l} ${h} \u2192 ${y} (${c.timestamp.split("T")[0]})`)}}if(i.length>0){console.log(`
79
+ Recent drift findings: ${i.length}`);for(const c of i.slice(-3)){const s=c.data,l=s.severity??"?",g=s.description??"";console.log(` [${l}] ${typeof g=="string"?g.slice(0,80):g}`)}}await n.close()}async function Dt(e,o){try{const n=await de({force:e,port:o});if(n.created.length>0){console.log("Created:");for(const t of n.created)console.log(` ${t}`)}if(n.skipped.length>0){console.log("Skipped (already exists \u2014 use --force to overwrite):");for(const t of n.skipped)console.log(` ${t}`)}if(n.merged.length>0){console.log("Merged:");for(const t of n.merged)console.log(` ${t}`)}if(n.errors.length>0){console.log("Errors:");for(const t of n.errors)console.log(` ${t}`)}n.errors.length===0&&console.log(`
80
+ Sentinel + Claude Code integration ready.`)}catch(n){console.error(`Init failed: ${n.message}`),process.exit(1)}}if(Le){const e=w(f,"--port");await Dt(qe,e?parseInt(e,10):void 0)}else if(Ye&&p&&Q&&V)await vt(p,Q,V);else if(Xe&&p)await At(p);else if(Ze&&p)await It(p);else if(et&&p)await St(p);else if(Oe)await re();else if(_)await le(_);else if(Ge&&p)await yt(p);else if(Je&&p)await ht(p);else if(We&&p)await wt(p);else if(Ue)await $t();else if(_e&&p)await tt(p,X??"No reason provided");else if(Qe&&p)await nt(p,X??"No reason provided");else if(Ve)await st(p);else if(ze)await it();else if(Pe)await gt();else if(xe)await ut(p);else if(Ne&&p&&U)await dt(p,U,ke);else if(Fe&&p)await ct(p);else if(Be){const e=j?parseInt(j,10):30;await pt(e,Z==="json"?"json":"markdown")}else if(He&&p){const e=j?parseInt(j,10):30;await ft(p,e,Z==="json"?"json":"markdown")}else Ke?await mt():p?await lt(p):await rt();
@@ -1,4 +1,4 @@
1
- import { v as Sentinel, e as AgentRole, S as SecurityFinding } from '../Sentinel-B_sv8Kiy.js';
1
+ import { v as Sentinel, e as AgentRole, S as SecurityFinding } from '../Sentinel-xFCyXH45.js';
2
2
  import 'node:crypto';
3
3
 
4
4
  /**
@@ -49,6 +49,32 @@ interface SentinelGatewayOptions {
49
49
  operatorCeiling?: AgentRole;
50
50
  /** Home dir bounding workspace policy discovery (B-path only). Defaults to os.homedir(). */
51
51
  home?: string;
52
+ /**
53
+ * Operator token for the loopback /release route (Sprint 0.1.1). The daemon
54
+ * generates one per start and passes it here. When unset, the /release route
55
+ * is disabled (503) — there is no unauthenticated release path.
56
+ */
57
+ releaseToken?: string;
58
+ /**
59
+ * Sprint 26 Gate-A Item D (F-8) — disposition for tool names outside the
60
+ * recognized native set (MCP-shaped names never reach this; they have their
61
+ * own translation path). "warn" (default) allows the call and persists an
62
+ * observational unknown_tool finding to the audit trail — no unknown tool
63
+ * passes unlogged; "deny" is the hardened opt-in that blocks the call with
64
+ * a non-escalation-eligible unknown_tool finding. warn ships as the default
65
+ * because the recognized set is inventoried, not provably complete — the
66
+ * default flips to deny after warn-mode dogfooding reconciles the inventory
67
+ * against live traffic. Operator-only: sourced from the launch --policy
68
+ * yaml; workspace yamls contribute roles via the ceiling merge, never
69
+ * gateway config.
70
+ */
71
+ unknownTools?: "deny" | "warn";
72
+ /**
73
+ * Item D escape hatch: native-shaped names to treat as known
74
+ * tool_invocation (e.g. a cc tool newer than this Sentinel build).
75
+ * Operator-only, same channel as unknownTools.
76
+ */
77
+ allowUnknownTools?: string[];
52
78
  }
53
79
  declare class SentinelGateway {
54
80
  private readonly configuredPort;
@@ -61,6 +87,9 @@ declare class SentinelGateway {
61
87
  private readonly workspaceIsolation;
62
88
  private readonly operatorCeiling;
63
89
  private readonly home;
90
+ private readonly releaseToken;
91
+ /** Item D (F-8): disposition for unknown (non-MCP, unrecognized) tool names. */
92
+ private readonly unknownTools;
64
93
  private server;
65
94
  private running;
66
95
  private signalHandlersInstalled;
@@ -93,6 +122,13 @@ declare class SentinelGateway {
93
122
  private removeSignalHandlers;
94
123
  private handleSignal;
95
124
  private handleRequest;
125
+ /**
126
+ * Operator release route (Sprint 0.1.1). Loopback-only + token-gated. On a
127
+ * valid request, calls sentinel.release() on the LIVE instance — flipping the
128
+ * in-memory mode, writing mode.json, and logging the signed mode_change anchor
129
+ * in-process (single writer). Never re-reads mode.json or trusts file content.
130
+ */
131
+ private handleReleaseRoute;
96
132
  /**
97
133
  * Emit a workspace-mismatch refusal as a finding + telemetry. Originally
98
134
  * Approach A's foreign-refuse emitter; after the B5b-Phase-2 cutover this is
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  SentinelGateway
3
- } from "../chunk-Z3PWIJKT.js";
3
+ } from "../chunk-L4R3LPJS.js";
4
+ import "../chunk-B5QKJHSV.js";
4
5
  import "../chunk-FMZWHT4M.js";
5
- import "../chunk-6MHWJATS.js";
6
- import "../chunk-2FFMYSVC.js";
6
+ import "../chunk-QIYQWOLO.js";
7
+ import "../chunk-WLIDSTS4.js";
7
8
  export {
8
9
  SentinelGateway
9
10
  };
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runGatewayDaemon
4
- } from "./chunk-Z3PWIJKT.js";
4
+ } from "./chunk-L4R3LPJS.js";
5
+ import "./chunk-B5QKJHSV.js";
5
6
  import "./chunk-FMZWHT4M.js";
6
- import "./chunk-6MHWJATS.js";
7
- import "./chunk-2FFMYSVC.js";
7
+ import "./chunk-QIYQWOLO.js";
8
+ import "./chunk-WLIDSTS4.js";
8
9
 
9
10
  // src/gatewayDaemon.ts
10
11
  var args = process.argv.slice(2);
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AgentActivityEvent, S as SecurityFinding } from './Sentinel-B_sv8Kiy.js';
2
- export { a as AcceptableAction, b as AdapterConfig, c as AgentBaseline, d as AgentMode, e as AgentRole, f as AlertChannel, g as AlertConfig, h as AllowResponse, i as AuditEntry, j as AuditQueryOptions, B as BlockResponse, C as CorrelationFinding, E as ExceptionApprovalContext, k as ExceptionApprovalFn, G as GuideResponse, H as HookCheckpoint, l as HookContext, m as HookHandler, n as HookRegistration, o as HookResponse, I as IntentAlignmentConfig, p as IntentAlignmentResult, M as ModifiableEventFields, q as MonitorOptions, O as OverlayDecisionType, R as RepoSensitivityMap, r as ReportOptions, s as RoleException, t as SecuritySeverity, u as SensitivityOverlay, v as Sentinel, w as SentinelConfig, T as TaskIntent } from './Sentinel-B_sv8Kiy.js';
1
+ import { A as AgentActivityEvent, S as SecurityFinding } from './Sentinel-xFCyXH45.js';
2
+ export { a as AcceptableAction, b as AdapterConfig, c as AgentBaseline, d as AgentMode, e as AgentRole, f as AlertChannel, g as AlertConfig, h as AllowResponse, i as AuditEntry, j as AuditQueryOptions, B as BlockResponse, C as CorrelationFinding, E as ExceptionApprovalContext, k as ExceptionApprovalFn, G as GuideResponse, H as HookCheckpoint, l as HookContext, m as HookHandler, n as HookRegistration, o as HookResponse, I as IntentAlignmentConfig, p as IntentAlignmentResult, M as ModifiableEventFields, q as MonitorOptions, O as OverlayDecisionType, R as RepoSensitivityMap, r as ReportOptions, s as RoleException, t as SecuritySeverity, u as SensitivityOverlay, v as Sentinel, w as SentinelConfig, T as TaskIntent } from './Sentinel-xFCyXH45.js';
3
3
  import 'node:crypto';
4
4
 
5
5
  interface SentinelPolicy {
@@ -57,6 +57,15 @@ interface SentinelPolicy {
57
57
  minDaysObserved?: number;
58
58
  minCategoryDiversity?: number;
59
59
  };
60
+ /**
61
+ * Item D (F-8): disposition for tool names outside the recognized native
62
+ * set. Default "warn" (allow + persisted observational finding); "deny"
63
+ * is the hardened opt-in. Operator launch policy only — the gateway reads
64
+ * it once at start.
65
+ */
66
+ unknownTools?: "deny" | "warn";
67
+ /** Item D escape hatch: native-shaped names to treat as known. */
68
+ allowUnknownTools?: string[];
60
69
  };
61
70
  alerts?: {
62
71
  channels: (string | {
package/dist/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  runInitClaudeCode,
3
3
  runSessionStart
4
- } from "./chunk-3U3PKD4N.js";
4
+ } from "./chunk-FWIISAZZ.js";
5
5
  import {
6
6
  Sentinel,
7
7
  createCliApproval
8
- } from "./chunk-QFRDEISP.js";
9
- import "./chunk-CUJKNIKT.js";
8
+ } from "./chunk-GRN5P3H2.js";
9
+ import "./chunk-LATQNIRW.js";
10
10
  import {
11
11
  discoverPolicy
12
12
  } from "./chunk-FMZWHT4M.js";
13
- import "./chunk-6MHWJATS.js";
13
+ import "./chunk-QIYQWOLO.js";
14
14
  import {
15
15
  loadPolicy,
16
16
  loadPolicyFromString
17
- } from "./chunk-2FFMYSVC.js";
17
+ } from "./chunk-WLIDSTS4.js";
18
18
  import "./chunk-NUXSUSYY.js";
19
19
  export {
20
20
  Sentinel,
@@ -0,0 +1,23 @@
1
+ import {
2
+ acquireGatewayLock,
3
+ readPidFile,
4
+ readReleaseToken,
5
+ releaseTokenPath,
6
+ removePidFile,
7
+ removeReleaseToken,
8
+ verifyPidIsGateway,
9
+ writePidFile,
10
+ writeReleaseToken
11
+ } from "./chunk-LATQNIRW.js";
12
+ export {
13
+ acquireGatewayLock,
14
+ readPidFile,
15
+ readReleaseToken,
16
+ releaseTokenPath,
17
+ removePidFile,
18
+ removeReleaseToken,
19
+ verifyPidIsGateway,
20
+ writePidFile,
21
+ writeReleaseToken
22
+ };
23
+ //# sourceMappingURL=pidManager-DOGVN6ZT.js.map
@@ -4,7 +4,7 @@ import {
4
4
  loadPolicyFromString,
5
5
  policyToConfig,
6
6
  policyToRole
7
- } from "./chunk-2FFMYSVC.js";
7
+ } from "./chunk-WLIDSTS4.js";
8
8
  export {
9
9
  LOCKED_ACTIONABLE_TYPES,
10
10
  loadPolicy,
@@ -12,4 +12,4 @@ export {
12
12
  policyToConfig,
13
13
  policyToRole
14
14
  };
15
- //# sourceMappingURL=policyLoader-6KR5VFVV.js.map
15
+ //# sourceMappingURL=policyLoader-KZL2U4M2.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuent/sentinel",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "AI agent behavioral security monitoring SDK",
5
5
  "author": "Tuent LLC",
6
6
  "keywords": [
@@ -31,7 +31,8 @@
31
31
  "dist",
32
32
  "!dist/**/*.map",
33
33
  "LICENSE",
34
- "README.md"
34
+ "README.md",
35
+ "SECURITY_MODEL.md"
35
36
  ],
36
37
  "engines": {
37
38
  "node": ">=20"
@@ -1,10 +0,0 @@
1
- import {
2
- Sentinel
3
- } from "./chunk-QFRDEISP.js";
4
- import "./chunk-6MHWJATS.js";
5
- import "./chunk-2FFMYSVC.js";
6
- import "./chunk-NUXSUSYY.js";
7
- export {
8
- Sentinel
9
- };
10
- //# sourceMappingURL=Sentinel-JLQL3YRD.js.map
@@ -1,15 +0,0 @@
1
- import {
2
- acquireGatewayLock,
3
- readPidFile,
4
- removePidFile,
5
- verifyPidIsGateway,
6
- writePidFile
7
- } from "./chunk-CUJKNIKT.js";
8
- export {
9
- acquireGatewayLock,
10
- readPidFile,
11
- removePidFile,
12
- verifyPidIsGateway,
13
- writePidFile
14
- };
15
- //# sourceMappingURL=pidManager-ZYC7SICM.js.map