@agenticmail/enterprise 0.5.165 → 0.5.167

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.
@@ -2787,14 +2787,14 @@ function GuardrailsSection(props) {
2787
2787
  var agents = props.agents || [];
2788
2788
  var app = useApp();
2789
2789
  var toast = app.toast;
2790
-
2791
2790
  var agentData = buildAgentDataMap(agents);
2792
2791
 
2793
- var _subTab = useState('status');
2792
+ var _subTab = useState('rules');
2794
2793
  var subTab = _subTab[0]; var setSubTab = _subTab[1];
2795
-
2796
2794
  var _guardrailStatus = useState(null);
2797
2795
  var guardrailStatus = _guardrailStatus[0]; var setGuardrailStatus = _guardrailStatus[1];
2796
+ var _rules = useState([]);
2797
+ var rules = _rules[0]; var setRules = _rules[1];
2798
2798
  var _interventions = useState([]);
2799
2799
  var interventions = _interventions[0]; var setInterventions = _interventions[1];
2800
2800
  var _dlpViolations = useState([]);
@@ -2803,33 +2803,55 @@ function GuardrailsSection(props) {
2803
2803
  var onboardingStatus = _onboardingStatus[0]; var setOnboardingStatus = _onboardingStatus[1];
2804
2804
  var _onboardingProgress = useState([]);
2805
2805
  var onboardingProgress = _onboardingProgress[0]; var setOnboardingProgress = _onboardingProgress[1];
2806
- var _pendingPolicies = useState([]);
2807
- var pendingPolicies = _pendingPolicies[0]; var setPendingPolicies = _pendingPolicies[1];
2808
2806
  var _pendingApprovals = useState([]);
2809
2807
  var pendingApprovals = _pendingApprovals[0]; var setPendingApprovals = _pendingApprovals[1];
2810
2808
  var _approvalHistory = useState([]);
2811
2809
  var approvalHistory = _approvalHistory[0]; var setApprovalHistory = _approvalHistory[1];
2812
2810
  var _loading = useState(true);
2813
2811
  var loading = _loading[0]; var setLoading = _loading[1];
2812
+ var _showCreate = useState(false);
2813
+ var showCreate = _showCreate[0]; var setShowCreate = _showCreate[1];
2814
+ var _editRule = useState(null);
2815
+ var editRule = _editRule[0]; var setEditRule = _editRule[1];
2816
+ var _ruleForm = useState({ name: '', category: 'anomaly', ruleType: 'error_rate', action: 'alert', severity: 'medium', enabled: true, threshold: 10, windowMinutes: 60, cooldownMinutes: 30, keywords: '', patterns: '', description: '' });
2817
+ var ruleForm = _ruleForm[0]; var setRuleForm = _ruleForm[1];
2818
+
2819
+ var CATEGORIES = [
2820
+ { value: 'anomaly', label: 'Anomaly Detection', desc: 'Unusual patterns in agent behavior', types: ['error_rate', 'cost_velocity', 'volume_spike', 'off_hours', 'session_anomaly'] },
2821
+ { value: 'policy_compliance', label: 'Policy Compliance', desc: 'Ensure agents follow org policies', types: ['policy_violation', 'escalation_failure'] },
2822
+ { value: 'communication', label: 'Communication', desc: 'Monitor communication quality', types: ['tone_violation', 'keyword_detection'] },
2823
+ { value: 'memory', label: 'Memory', desc: 'Control memory write behavior', types: ['memory_flood'] },
2824
+ { value: 'onboarding', label: 'Onboarding', desc: 'Enforce onboarding requirements', types: ['onboarding_bypass'] },
2825
+ { value: 'security', label: 'Security', desc: 'Detect threats and suspicious patterns', types: ['data_leak_attempt', 'repeated_error', 'prompt_injection'] }
2826
+ ];
2827
+
2828
+ var TYPE_LABELS = {
2829
+ error_rate: 'Error Rate', cost_velocity: 'Cost Velocity', volume_spike: 'Volume Spike',
2830
+ off_hours: 'Off-Hours Activity', session_anomaly: 'Session Anomaly',
2831
+ policy_violation: 'Policy Violation', escalation_failure: 'Escalation Failure',
2832
+ tone_violation: 'Tone Violation', keyword_detection: 'Keyword Detection',
2833
+ memory_flood: 'Memory Flood', onboarding_bypass: 'Onboarding Bypass',
2834
+ data_leak_attempt: 'Data Leak Attempt', repeated_error: 'Repeated Error', prompt_injection: 'Prompt Injection'
2835
+ };
2814
2836
 
2815
2837
  var loadAll = function() {
2816
2838
  setLoading(true);
2817
2839
  Promise.all([
2818
2840
  engineCall('/guardrails/status/' + agentId).catch(function() { return null; }),
2841
+ engineCall('/guardrails/rules?orgId=' + getOrgId()).catch(function() { return { rules: [] }; }),
2819
2842
  engineCall('/guardrails/interventions?agentId=' + agentId).catch(function() { return { interventions: [] }; }),
2820
2843
  engineCall('/dlp/violations?agentId=' + agentId).catch(function() { return { violations: [] }; }),
2821
2844
  engineCall('/onboarding/status/' + agentId).catch(function() { return null; }),
2822
2845
  engineCall('/onboarding/progress/' + agentId).catch(function() { return { progress: [] }; }),
2823
- engineCall('/onboarding/pending/' + agentId).catch(function() { return { policies: [] }; }),
2824
2846
  engineCall('/approvals/pending?agentId=' + agentId).catch(function() { return { approvals: [] }; }),
2825
2847
  engineCall('/approvals/history?agentId=' + agentId).catch(function() { return { approvals: [] }; })
2826
2848
  ]).then(function(results) {
2827
2849
  setGuardrailStatus(results[0]);
2828
- setInterventions(results[1]?.interventions || results[1] || []);
2829
- setDlpViolations(results[2]?.violations || results[2] || []);
2830
- setOnboardingStatus(results[3]);
2831
- setOnboardingProgress(results[4]?.progress || results[4] || []);
2832
- setPendingPolicies(results[5]?.policies || results[5] || []);
2850
+ setRules(results[1]?.rules || []);
2851
+ setInterventions(results[2]?.interventions || results[2] || []);
2852
+ setDlpViolations(results[3]?.violations || results[3] || []);
2853
+ setOnboardingStatus(results[4]);
2854
+ setOnboardingProgress(results[5]?.progress || results[5] || []);
2833
2855
  setPendingApprovals(results[6]?.approvals || results[6] || []);
2834
2856
  setApprovalHistory(results[7]?.approvals || results[7] || []);
2835
2857
  setLoading(false);
@@ -2838,67 +2860,146 @@ function GuardrailsSection(props) {
2838
2860
 
2839
2861
  useEffect(function() { loadAll(); }, [agentId]);
2840
2862
 
2863
+ // Agent-relevant rules: either targets this agent specifically, or applies globally (no agentIds filter)
2864
+ var agentRules = rules.filter(function(r) {
2865
+ if (!r.conditions?.agentIds || r.conditions.agentIds.length === 0) return true;
2866
+ return r.conditions.agentIds.includes(agentId);
2867
+ });
2868
+
2841
2869
  var pauseAgent = function() {
2842
2870
  engineCall('/guardrails/pause/' + agentId, { method: 'POST', body: JSON.stringify({ reason: 'Manual pause from dashboard' }) })
2843
2871
  .then(function() { toast('Agent paused', 'success'); loadAll(); })
2844
2872
  .catch(function(err) { toast(err.message, 'error'); });
2845
2873
  };
2846
-
2847
2874
  var resumeAgent = function() {
2848
2875
  engineCall('/guardrails/resume/' + agentId, { method: 'POST', body: JSON.stringify({ reason: 'Manual resume from dashboard' }) })
2849
2876
  .then(function() { toast('Agent resumed', 'success'); loadAll(); })
2850
2877
  .catch(function(err) { toast(err.message, 'error'); });
2851
2878
  };
2852
-
2853
2879
  var killAgent = function() {
2854
- showConfirm({
2855
- title: 'Kill Agent',
2856
- message: 'Are you sure you want to kill this agent? This will immediately terminate all running processes.',
2857
- warning: 'This action cannot be undone.',
2858
- danger: true,
2859
- confirmText: 'Kill Agent'
2860
- }).then(function(confirmed) {
2861
- if (!confirmed) return;
2880
+ showConfirm({ title: 'Kill Agent', message: 'Immediately terminate all running processes?', danger: true, confirmText: 'Kill Agent' }).then(function(ok) {
2881
+ if (!ok) return;
2862
2882
  engineCall('/guardrails/kill/' + agentId, { method: 'POST', body: JSON.stringify({ reason: 'Manual kill from dashboard' }) })
2863
2883
  .then(function() { toast('Agent killed', 'success'); loadAll(); })
2864
2884
  .catch(function(err) { toast(err.message, 'error'); });
2865
2885
  });
2866
2886
  };
2867
2887
 
2888
+ var toggleRule = function(rule) {
2889
+ engineCall('/guardrails/rules/' + rule.id, { method: 'PUT', body: JSON.stringify({ enabled: !rule.enabled }) })
2890
+ .then(function() { toast(rule.enabled ? 'Rule disabled' : 'Rule enabled', 'success'); loadAll(); })
2891
+ .catch(function(err) { toast(err.message, 'error'); });
2892
+ };
2893
+
2894
+ var deleteRule = function(rule) {
2895
+ showConfirm({ title: 'Delete Rule', message: 'Delete "' + rule.name + '"? This cannot be undone.', warning: true, confirmText: 'Delete' }).then(function(ok) {
2896
+ if (!ok) return;
2897
+ engineCall('/guardrails/rules/' + rule.id, { method: 'DELETE' })
2898
+ .then(function() { toast('Rule deleted', 'success'); loadAll(); })
2899
+ .catch(function(err) { toast(err.message, 'error'); });
2900
+ });
2901
+ };
2902
+
2903
+ var openCreateRule = function() {
2904
+ setRuleForm({ name: '', category: 'anomaly', ruleType: 'error_rate', action: 'alert', severity: 'medium', enabled: true, threshold: 10, windowMinutes: 60, cooldownMinutes: 30, keywords: '', patterns: '', description: '' });
2905
+ setEditRule(null);
2906
+ setShowCreate(true);
2907
+ };
2908
+
2909
+ var openEditRule = function(rule) {
2910
+ setRuleForm({
2911
+ name: rule.name || '',
2912
+ category: rule.category || 'anomaly',
2913
+ ruleType: rule.ruleType || 'error_rate',
2914
+ action: rule.action || 'alert',
2915
+ severity: rule.severity || 'medium',
2916
+ enabled: rule.enabled !== false,
2917
+ threshold: rule.conditions?.threshold || 10,
2918
+ windowMinutes: rule.conditions?.windowMinutes || 60,
2919
+ cooldownMinutes: rule.cooldownMinutes || 30,
2920
+ keywords: (rule.conditions?.keywords || []).join(', '),
2921
+ patterns: (rule.conditions?.patterns || []).join(', '),
2922
+ description: rule.description || ''
2923
+ });
2924
+ setEditRule(rule);
2925
+ setShowCreate(true);
2926
+ };
2927
+
2928
+ var saveRule = function() {
2929
+ var body = {
2930
+ orgId: getOrgId(),
2931
+ name: ruleForm.name,
2932
+ description: ruleForm.description,
2933
+ category: ruleForm.category,
2934
+ ruleType: ruleForm.ruleType,
2935
+ action: ruleForm.action,
2936
+ severity: ruleForm.severity,
2937
+ enabled: ruleForm.enabled,
2938
+ cooldownMinutes: parseInt(ruleForm.cooldownMinutes) || 30,
2939
+ conditions: {
2940
+ agentIds: [agentId],
2941
+ threshold: parseFloat(ruleForm.threshold) || undefined,
2942
+ windowMinutes: parseInt(ruleForm.windowMinutes) || undefined,
2943
+ keywords: ruleForm.keywords ? ruleForm.keywords.split(',').map(function(k) { return k.trim(); }).filter(Boolean) : undefined,
2944
+ patterns: ruleForm.patterns ? ruleForm.patterns.split(',').map(function(p) { return p.trim(); }).filter(Boolean) : undefined
2945
+ }
2946
+ };
2947
+ var method = editRule ? 'PUT' : 'POST';
2948
+ var url = editRule ? '/guardrails/rules/' + editRule.id : '/guardrails/rules';
2949
+ engineCall(url, { method: method, body: JSON.stringify(body) })
2950
+ .then(function() { toast(editRule ? 'Rule updated' : 'Rule created', 'success'); setShowCreate(false); loadAll(); })
2951
+ .catch(function(err) { toast(err.message, 'error'); });
2952
+ };
2953
+
2868
2954
  var initiateOnboarding = function() {
2869
2955
  engineCall('/onboarding/initiate/' + agentId, { method: 'POST', body: JSON.stringify({ orgId: getOrgId() }) })
2870
2956
  .then(function() { toast('Onboarding initiated', 'success'); loadAll(); })
2871
2957
  .catch(function(err) { toast(err.message, 'error'); });
2872
2958
  };
2873
-
2874
2959
  var forceComplete = function() {
2875
2960
  engineCall('/onboarding/force-complete/' + agentId, { method: 'POST' })
2876
2961
  .then(function() { toast('Onboarding force completed', 'success'); loadAll(); })
2877
2962
  .catch(function(err) { toast(err.message, 'error'); });
2878
2963
  };
2879
-
2880
2964
  var approveRequest = function(id) {
2881
2965
  engineCall('/approvals/' + id + '/approve', { method: 'POST', body: JSON.stringify({ decidedBy: 'dashboard-admin' }) })
2882
- .then(function() { toast('Request approved', 'success'); loadAll(); })
2966
+ .then(function() { toast('Approved', 'success'); loadAll(); })
2883
2967
  .catch(function(err) { toast(err.message, 'error'); });
2884
2968
  };
2885
-
2886
2969
  var rejectRequest = function(id) {
2887
2970
  engineCall('/approvals/' + id + '/reject', { method: 'POST', body: JSON.stringify({ decidedBy: 'dashboard-admin' }) })
2888
- .then(function() { toast('Request rejected', 'success'); loadAll(); })
2971
+ .then(function() { toast('Rejected', 'success'); loadAll(); })
2889
2972
  .catch(function(err) { toast(err.message, 'error'); });
2890
2973
  };
2891
2974
 
2892
- if (loading) {
2893
- return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading guardrails data...');
2894
- }
2975
+ var actionColor = function(a) { return a === 'kill' ? 'var(--danger)' : a === 'pause' ? 'var(--warning)' : a === 'notify' ? 'var(--info)' : 'var(--text-muted)'; };
2976
+ var sevColor = function(s) { return s === 'critical' ? '#ef4444' : s === 'high' ? '#f97316' : s === 'medium' ? '#eab308' : '#64748b'; };
2977
+ var catIcon = function(c) { return c === 'anomaly' ? '⚡' : c === 'security' ? '🛡' : c === 'communication' ? '💬' : c === 'memory' ? '🧠' : c === 'onboarding' ? '📋' : c === 'policy_compliance' ? '📜' : '⚙'; };
2978
+
2979
+ if (loading) return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading guardrails...');
2895
2980
 
2896
2981
  return h(Fragment, null,
2897
2982
 
2898
- // ─── Sub-Tab Bar ────────────────────────────────────
2899
- h('div', { style: { borderBottom: '1px solid var(--border)', marginBottom: 20 } },
2983
+ // Status bar
2984
+ h('div', { className: 'card', style: { marginBottom: 16 } },
2985
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, padding: '10px 16px' } },
2986
+ guardrailStatus && guardrailStatus.paused
2987
+ ? h('span', { className: 'badge badge-warning' }, I.pause(), ' Paused')
2988
+ : h('span', { className: 'badge badge-success' }, I.shield(), ' Active'),
2989
+ h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, (guardrailStatus?.interventionCount || 0) + ' interventions'),
2990
+ agentRules.length > 0 && h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, agentRules.filter(function(r) { return r.enabled; }).length + '/' + agentRules.length + ' rules active'),
2991
+ h('div', { style: { flex: 1 } }),
2992
+ guardrailStatus && !guardrailStatus.paused && h('button', { className: 'btn btn-secondary btn-sm', onClick: pauseAgent }, I.pause(), ' Pause'),
2993
+ guardrailStatus && guardrailStatus.paused && h('button', { className: 'btn btn-primary btn-sm', onClick: resumeAgent }, I.play(), ' Resume'),
2994
+ h('button', { className: 'btn btn-danger btn-sm', onClick: killAgent }, I.stop(), ' Kill'),
2995
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: loadAll }, I.refresh())
2996
+ )
2997
+ ),
2998
+
2999
+ // Sub-tabs
3000
+ h('div', { style: { borderBottom: '1px solid var(--border)', marginBottom: 16 } },
2900
3001
  h('div', { className: 'tabs' },
2901
- h('div', { className: 'tab' + (subTab === 'status' ? ' active' : ''), onClick: function() { setSubTab('status'); } }, 'Status'),
3002
+ h('div', { className: 'tab' + (subTab === 'rules' ? ' active' : ''), onClick: function() { setSubTab('rules'); } }, 'Rules (' + agentRules.length + ')'),
2902
3003
  h('div', { className: 'tab' + (subTab === 'interventions' ? ' active' : ''), onClick: function() { setSubTab('interventions'); } }, 'Interventions'),
2903
3004
  h('div', { className: 'tab' + (subTab === 'dlp' ? ' active' : ''), onClick: function() { setSubTab('dlp'); } }, 'DLP'),
2904
3005
  h('div', { className: 'tab' + (subTab === 'onboarding' ? ' active' : ''), onClick: function() { setSubTab('onboarding'); } }, 'Onboarding'),
@@ -2906,258 +3007,268 @@ function GuardrailsSection(props) {
2906
3007
  )
2907
3008
  ),
2908
3009
 
2909
- // ─── Status Tab ─────────────────────────────────────
2910
- subTab === 'status' && h('div', null,
2911
- h('div', { className: 'card', style: { marginBottom: 20 } },
2912
- h('div', { className: 'card-header' }, h('span', null, 'Guardrail Status')),
2913
- h('div', { className: 'card-body' },
2914
- guardrailStatus && guardrailStatus.paused
2915
- ? h('div', { style: { padding: '12px 16px', background: 'rgba(234, 179, 8, 0.1)', border: '1px solid var(--warning)', borderRadius: 8, marginBottom: 16 } },
2916
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 } },
2917
- h('span', { className: 'badge badge-warning' }, I.pause(), ' Paused'),
2918
- guardrailStatus.pausedAt && h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Since: ' + new Date(guardrailStatus.pausedAt).toLocaleString())
3010
+ // ─── Rules Tab ──────────────────────────────────────
3011
+ subTab === 'rules' && h('div', null,
3012
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 } },
3013
+ h('span', { style: { fontSize: 13, color: 'var(--text-muted)' } }, 'Guardrail rules that apply to this agent (agent-specific + global)'),
3014
+ h('button', { className: 'btn btn-primary btn-sm', onClick: openCreateRule }, I.plus(), ' Add Rule')
3015
+ ),
3016
+
3017
+ // Category groups
3018
+ CATEGORIES.map(function(cat) {
3019
+ var catRules = agentRules.filter(function(r) { return r.category === cat.value; });
3020
+ if (catRules.length === 0) {
3021
+ // Show empty category with "Add" button
3022
+ return h('div', { key: cat.value, className: 'card', style: { marginBottom: 8 } },
3023
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '10px 16px' } },
3024
+ h('span', { style: { fontSize: 14 } }, catIcon(cat.value)),
3025
+ h('span', { style: { fontWeight: 600, fontSize: 13 } }, cat.label),
3026
+ h('span', { style: { fontSize: 11, color: 'var(--text-muted)', marginLeft: 4 } }, cat.desc),
3027
+ h('div', { style: { flex: 1 } }),
3028
+ h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'No rules'),
3029
+ h('button', { className: 'btn btn-ghost btn-sm', style: { fontSize: 11 }, onClick: function() { setRuleForm(Object.assign({}, ruleForm, { category: cat.value, ruleType: cat.types[0] })); setEditRule(null); setShowCreate(true); } }, I.plus(), ' Add')
3030
+ )
3031
+ );
3032
+ }
3033
+ return h('div', { key: cat.value, className: 'card', style: { marginBottom: 8 } },
3034
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '8px 16px', borderBottom: '1px solid var(--border)' } },
3035
+ h('span', { style: { fontSize: 14 } }, catIcon(cat.value)),
3036
+ h('span', { style: { fontWeight: 600, fontSize: 13 } }, cat.label),
3037
+ h('span', { style: { fontSize: 11, color: 'var(--text-muted)', marginLeft: 4 } }, catRules.length + ' rule' + (catRules.length !== 1 ? 's' : '')),
3038
+ h('div', { style: { flex: 1 } }),
3039
+ h('button', { className: 'btn btn-ghost btn-sm', style: { fontSize: 11 }, onClick: function() { setRuleForm(Object.assign({}, ruleForm, { category: cat.value, ruleType: cat.types[0] })); setEditRule(null); setShowCreate(true); } }, I.plus())
3040
+ ),
3041
+ catRules.map(function(rule) {
3042
+ var isGlobal = !rule.conditions?.agentIds || rule.conditions.agentIds.length === 0;
3043
+ return h('div', { key: rule.id, style: { display: 'flex', alignItems: 'center', gap: 10, padding: '8px 16px', borderBottom: '1px solid var(--border)', fontSize: 13 } },
3044
+ // Toggle switch
3045
+ h('div', {
3046
+ style: { width: 36, height: 20, borderRadius: 10, background: rule.enabled ? 'var(--success)' : 'var(--border)', cursor: 'pointer', position: 'relative', flexShrink: 0, transition: 'background 0.2s' },
3047
+ onClick: function() { toggleRule(rule); }
3048
+ },
3049
+ h('div', { style: { width: 16, height: 16, borderRadius: '50%', background: '#fff', position: 'absolute', top: 2, left: rule.enabled ? 18 : 2, transition: 'left 0.2s', boxShadow: '0 1px 3px rgba(0,0,0,0.2)' } })
3050
+ ),
3051
+ // Name + type
3052
+ h('div', { style: { flex: 1, minWidth: 0 } },
3053
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 6 } },
3054
+ h('span', { style: { fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, rule.name || TYPE_LABELS[rule.ruleType] || rule.ruleType),
3055
+ isGlobal && h('span', { style: { fontSize: 10, padding: '1px 5px', borderRadius: 3, background: 'var(--bg-tertiary)', color: 'var(--text-muted)', border: '1px solid var(--border)' } }, 'Global')
2919
3056
  ),
2920
- guardrailStatus.pauseReason && h('div', { style: { fontSize: 13, color: 'var(--text-secondary)', marginTop: 4 } }, 'Reason: ' + guardrailStatus.pauseReason)
2921
- )
2922
- : h('div', { style: { padding: '12px 16px', background: 'rgba(34, 197, 94, 0.1)', border: '1px solid var(--success)', borderRadius: 8, marginBottom: 16 } },
2923
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
2924
- h('span', { className: 'badge badge-success' }, I.shield(), ' Active')
2925
- )
3057
+ rule.description && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, rule.description)
2926
3058
  ),
2927
- h('div', { style: { display: 'flex', gap: 16, marginBottom: 16, fontSize: 13 } },
2928
- h('span', { style: { color: 'var(--text-muted)' } }, 'Interventions: ', h('strong', null, String(guardrailStatus?.interventionCount || guardrailStatus?.interventions || 0))),
2929
- guardrailStatus?.lastIntervention && h('span', { style: { color: 'var(--text-muted)' } }, 'Last: ' + new Date(guardrailStatus.lastIntervention).toLocaleString())
2930
- ),
2931
- h('div', { style: { display: 'flex', gap: 8 } },
2932
- guardrailStatus && !guardrailStatus.paused && h('button', { className: 'btn btn-secondary btn-sm', onClick: pauseAgent }, I.pause(), ' Pause'),
2933
- guardrailStatus && guardrailStatus.paused && h('button', { className: 'btn btn-primary btn-sm', onClick: resumeAgent }, I.play(), ' Resume'),
2934
- h('button', { className: 'btn btn-danger btn-sm', onClick: killAgent }, I.stop(), ' Kill')
2935
- )
2936
- )
2937
- )
3059
+ // Type badge
3060
+ h('span', { style: { fontSize: 10, padding: '2px 6px', borderRadius: 3, background: 'var(--bg-tertiary)', color: 'var(--text-secondary)', whiteSpace: 'nowrap' } }, TYPE_LABELS[rule.ruleType] || rule.ruleType),
3061
+ // Severity
3062
+ h('span', { style: { fontSize: 10, padding: '2px 6px', borderRadius: 3, color: '#fff', background: sevColor(rule.severity), whiteSpace: 'nowrap' } }, rule.severity),
3063
+ // Action
3064
+ h('span', { style: { fontSize: 11, color: actionColor(rule.action), fontWeight: 500 } }, rule.action),
3065
+ // Trigger count
3066
+ h('span', { style: { fontSize: 11, color: 'var(--text-muted)', minWidth: 40, textAlign: 'right' } }, (rule.triggerCount || 0) + 'x'),
3067
+ // Edit + Delete
3068
+ h('button', { className: 'btn btn-ghost btn-sm', style: { height: 24, fontSize: 11 }, onClick: function() { openEditRule(rule); } }, I.edit()),
3069
+ h('button', { className: 'btn btn-ghost btn-sm', style: { height: 24, fontSize: 11, color: 'var(--danger)' }, onClick: function() { deleteRule(rule); } }, I.trash())
3070
+ );
3071
+ })
3072
+ );
3073
+ })
2938
3074
  ),
2939
3075
 
2940
3076
  // ─── Interventions Tab ──────────────────────────────
2941
- subTab === 'interventions' && h('div', { className: 'card', style: { marginBottom: 20 } },
3077
+ subTab === 'interventions' && h('div', { className: 'card' },
2942
3078
  h('div', { className: 'card-header' }, h('span', null, 'Interventions')),
2943
3079
  interventions.length > 0
2944
- ? h('div', { className: 'card-body-flush' },
2945
- h('table', { className: 'data-table' },
2946
- h('thead', null,
2947
- h('tr', null,
2948
- h('th', null, 'Time'),
2949
- h('th', null, 'Type'),
2950
- h('th', null, 'Severity'),
2951
- h('th', null, 'Description'),
2952
- h('th', null, 'Resolution')
2953
- )
2954
- ),
2955
- h('tbody', null,
2956
- interventions.map(function(inv, i) {
2957
- var time = inv.timestamp || inv.createdAt;
2958
- var invType = inv.type || inv.interventionType || 'unknown';
2959
- var severity = inv.severity || 'medium';
2960
- var severityColor = severity === 'critical' ? 'badge-danger' : severity === 'high' ? 'badge-warning' : severity === 'medium' ? 'badge-info' : 'badge-neutral';
2961
- var typeColor = invType === 'block' ? 'badge-danger' : invType === 'warn' ? 'badge-warning' : invType === 'audit' ? 'badge-info' : 'badge-neutral';
2962
-
2963
- return h('tr', { key: inv.id || i },
2964
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)', whiteSpace: 'nowrap' } }, time ? new Date(time).toLocaleString() : '-'),
2965
- h('td', null, h('span', { className: 'badge ' + typeColor }, invType)),
2966
- h('td', null, h('span', { className: 'badge ' + severityColor }, severity)),
2967
- h('td', { style: { fontSize: 13, maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, inv.description || inv.message || '-'),
2968
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, inv.resolution || inv.action || '-')
2969
- );
2970
- })
2971
- )
2972
- )
2973
- )
2974
- : h('div', { className: 'card-body' },
2975
- h('div', { style: { textAlign: 'center', padding: 20, color: 'var(--text-muted)', fontSize: 13 } }, 'No interventions recorded.')
3080
+ ? h('div', { style: { padding: 0 } },
3081
+ interventions.map(function(inv, i) {
3082
+ var time = inv.timestamp || inv.createdAt;
3083
+ var invType = inv.type || inv.interventionType || 'unknown';
3084
+ var severity = inv.severity || 'medium';
3085
+ return h('div', { key: inv.id || i, style: { display: 'flex', gap: 10, padding: '8px 16px', borderBottom: '1px solid var(--border)', fontSize: 12, alignItems: 'center' } },
3086
+ h('span', { style: { color: 'var(--text-muted)', minWidth: 130, whiteSpace: 'nowrap' } }, time ? new Date(time).toLocaleString() : '-'),
3087
+ h('span', { style: { padding: '1px 6px', borderRadius: 3, fontSize: 10, fontWeight: 600, color: '#fff', background: invType === 'block' ? '#ef4444' : invType === 'warn' ? '#eab308' : '#3b82f6' } }, invType),
3088
+ h('span', { style: { padding: '1px 6px', borderRadius: 3, fontSize: 10, fontWeight: 600, color: '#fff', background: sevColor(severity) } }, severity),
3089
+ h('span', { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'var(--text-secondary)' } }, inv.description || inv.message || inv.reason || '-'),
3090
+ h('span', { style: { color: 'var(--text-muted)', fontSize: 11 } }, inv.resolution || inv.action || '')
3091
+ );
3092
+ })
2976
3093
  )
3094
+ : h('div', { style: { padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 } }, 'No interventions recorded')
2977
3095
  ),
2978
3096
 
2979
3097
  // ─── DLP Tab ────────────────────────────────────────
2980
- subTab === 'dlp' && h('div', { className: 'card', style: { marginBottom: 20 } },
3098
+ subTab === 'dlp' && h('div', { className: 'card' },
2981
3099
  h('div', { className: 'card-header' }, h('span', null, 'DLP Violations')),
2982
3100
  dlpViolations.length > 0
2983
- ? h('div', { className: 'card-body-flush' },
2984
- h('table', { className: 'data-table' },
2985
- h('thead', null,
2986
- h('tr', null,
2987
- h('th', null, 'Time'),
2988
- h('th', null, 'Rule'),
2989
- h('th', null, 'Severity'),
2990
- h('th', null, 'Content'),
2991
- h('th', null, 'Status')
2992
- )
2993
- ),
2994
- h('tbody', null,
2995
- dlpViolations.map(function(v, i) {
2996
- var time = v.timestamp || v.createdAt || v.detectedAt;
2997
- var rule = v.rule || v.ruleName || v.ruleId || 'Unknown';
2998
- var severity = v.severity || 'medium';
2999
- var severityColor = severity === 'critical' ? 'badge-danger' : severity === 'high' ? 'badge-warning' : severity === 'medium' ? 'badge-info' : 'badge-neutral';
3000
- var content = v.content || v.matchedContent || v.snippet || '';
3001
- var truncatedContent = content.length > 80 ? content.substring(0, 80) + '...' : content;
3002
- var violationStatus = v.status || v.action || 'detected';
3003
-
3004
- return h('tr', { key: v.id || i },
3005
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)', whiteSpace: 'nowrap' } }, time ? new Date(time).toLocaleString() : '-'),
3006
- h('td', null, h('span', { className: 'badge badge-info' }, rule)),
3007
- h('td', null, h('span', { className: 'badge ' + severityColor }, severity)),
3008
- h('td', { style: { fontSize: 12, fontFamily: 'var(--font-mono, monospace)', maxWidth: 250, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'var(--text-secondary)' } }, truncatedContent || '-'),
3009
- h('td', null, h('span', { className: 'badge badge-neutral' }, violationStatus))
3010
- );
3011
- })
3012
- )
3013
- )
3014
- )
3015
- : h('div', { className: 'card-body' },
3016
- h('div', { style: { textAlign: 'center', padding: 20, color: 'var(--text-muted)', fontSize: 13 } }, 'No DLP violations detected.')
3101
+ ? h('div', { style: { padding: 0 } },
3102
+ dlpViolations.map(function(v, i) {
3103
+ var time = v.timestamp || v.createdAt || v.detectedAt;
3104
+ var severity = v.severity || 'medium';
3105
+ return h('div', { key: v.id || i, style: { display: 'flex', gap: 10, padding: '8px 16px', borderBottom: '1px solid var(--border)', fontSize: 12, alignItems: 'center' } },
3106
+ h('span', { style: { color: 'var(--text-muted)', minWidth: 130, whiteSpace: 'nowrap' } }, time ? new Date(time).toLocaleString() : '-'),
3107
+ h('span', { style: { padding: '1px 6px', borderRadius: 3, fontSize: 10, fontWeight: 600, color: '#fff', background: '#3b82f6' } }, v.rule || v.ruleName || 'Unknown'),
3108
+ h('span', { style: { padding: '1px 6px', borderRadius: 3, fontSize: 10, fontWeight: 600, color: '#fff', background: sevColor(severity) } }, severity),
3109
+ h('span', { style: { flex: 1, fontFamily: 'var(--font-mono, monospace)', fontSize: 11, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'var(--text-secondary)' } }, (v.content || v.matchedContent || v.snippet || '-').substring(0, 100)),
3110
+ h('span', { className: 'badge badge-neutral', style: { fontSize: 10 } }, v.status || v.action || 'detected')
3111
+ );
3112
+ })
3017
3113
  )
3114
+ : h('div', { style: { padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 } }, 'No DLP violations detected')
3018
3115
  ),
3019
3116
 
3020
3117
  // ─── Onboarding Tab ─────────────────────────────────
3021
3118
  subTab === 'onboarding' && h('div', null,
3022
-
3023
- // Onboarding Status Card
3024
- h('div', { className: 'card', style: { marginBottom: 20 } },
3025
- h('div', { className: 'card-header', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
3026
- h('span', null, 'Onboarding Status'),
3027
- h('div', { style: { display: 'flex', gap: 8 } },
3028
- !onboardingStatus?.onboarded && h('button', { className: 'btn btn-primary btn-sm', onClick: initiateOnboarding }, 'Start Onboarding'),
3029
- onboardingStatus && onboardingStatus.status === 'in_progress' && h('button', { className: 'btn btn-secondary btn-sm', onClick: forceComplete }, 'Force Complete')
3030
- )
3031
- ),
3032
- h('div', { className: 'card-body' },
3033
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 } },
3034
- onboardingStatus?.onboarded
3035
- ? h('span', { className: 'badge badge-success' }, I.check(), ' Onboarded')
3036
- : h('span', { className: 'badge badge-warning' }, 'Not Onboarded'),
3037
- onboardingStatus?.status && h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Status: ' + onboardingStatus.status),
3038
- onboardingStatus?.completedAt && h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Completed: ' + new Date(onboardingStatus.completedAt).toLocaleString())
3039
- )
3119
+ h('div', { className: 'card', style: { marginBottom: 12 } },
3120
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, padding: '10px 16px' } },
3121
+ onboardingStatus?.onboarded
3122
+ ? h('span', { className: 'badge badge-success' }, I.check(), ' Onboarded')
3123
+ : h('span', { className: 'badge badge-warning' }, 'Not Onboarded'),
3124
+ onboardingStatus?.completedAt && h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Completed: ' + new Date(onboardingStatus.completedAt).toLocaleString()),
3125
+ h('div', { style: { flex: 1 } }),
3126
+ !onboardingStatus?.onboarded && h('button', { className: 'btn btn-primary btn-sm', onClick: initiateOnboarding }, 'Start'),
3127
+ onboardingStatus?.status === 'in_progress' && h('button', { className: 'btn btn-secondary btn-sm', onClick: forceComplete }, 'Force Complete')
3040
3128
  )
3041
3129
  ),
3042
-
3043
- // Progress Table
3044
- onboardingProgress.length > 0 && h('div', { className: 'card', style: { marginBottom: 20 } },
3045
- h('div', { className: 'card-header' }, h('span', null, 'Onboarding Progress')),
3046
- h('div', { className: 'card-body-flush' },
3047
- h('table', { className: 'data-table' },
3048
- h('thead', null,
3049
- h('tr', null,
3050
- h('th', null, 'Policy'),
3051
- h('th', null, 'Status'),
3052
- h('th', null, 'Acknowledged')
3053
- )
3054
- ),
3055
- h('tbody', null,
3056
- onboardingProgress.map(function(p, i) {
3057
- var pStatus = p.status || 'pending';
3058
- var statusColor = pStatus === 'acknowledged' || pStatus === 'completed' ? 'badge-success' : pStatus === 'in_progress' ? 'badge-info' : 'badge-warning';
3059
-
3060
- return h('tr', { key: p.id || i },
3061
- h('td', { style: { fontWeight: 500, fontSize: 13 } }, p.policyName || p.name || p.policyId || '-'),
3062
- h('td', null, h('span', { className: 'badge ' + statusColor }, pStatus)),
3063
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, p.acknowledgedAt ? new Date(p.acknowledgedAt).toLocaleString() : '-')
3064
- );
3065
- })
3066
- )
3067
- )
3068
- )
3069
- ),
3070
-
3071
- // Pending Policies
3072
- pendingPolicies.length > 0 && h('div', { className: 'card', style: { marginBottom: 20 } },
3073
- h('div', { className: 'card-header' }, h('span', null, 'Pending Policies')),
3074
- h('div', { className: 'card-body' },
3075
- h('div', { style: { display: 'grid', gap: 8 } },
3076
- pendingPolicies.map(function(p, i) {
3077
- return h('div', { key: p.id || i, style: { display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px', background: 'var(--bg-tertiary)', borderRadius: 8, border: '1px solid var(--border)' } },
3078
- h('span', { className: 'badge badge-warning' }, 'Pending'),
3079
- h('span', { style: { fontSize: 13, fontWeight: 500 } }, p.name || p.policyName || p.policyId || 'Unnamed Policy'),
3080
- p.category && h('span', { className: 'badge badge-neutral', style: { fontSize: 11 } }, p.category)
3081
- );
3082
- })
3083
- )
3130
+ onboardingProgress.length > 0 && h('div', { className: 'card' },
3131
+ h('div', { style: { padding: 0 } },
3132
+ onboardingProgress.map(function(p, i) {
3133
+ return h('div', { key: i, style: { display: 'flex', gap: 10, padding: '8px 16px', borderBottom: '1px solid var(--border)', fontSize: 12, alignItems: 'center' } },
3134
+ p.acknowledged
3135
+ ? h('span', { style: { color: 'var(--success)', fontSize: 14 } }, '✓')
3136
+ : h('span', { style: { color: 'var(--text-muted)', fontSize: 14 } }, '○'),
3137
+ h('span', { style: { fontWeight: 500, flex: 1 } }, p.policyName || p.name || 'Policy ' + (i + 1)),
3138
+ p.acknowledgedAt && h('span', { style: { color: 'var(--text-muted)', fontSize: 11 } }, new Date(p.acknowledgedAt).toLocaleDateString())
3139
+ );
3140
+ })
3084
3141
  )
3085
3142
  )
3086
3143
  ),
3087
3144
 
3088
3145
  // ─── Approvals Tab ──────────────────────────────────
3089
3146
  subTab === 'approvals' && h('div', null,
3090
-
3091
- // Pending Approvals
3092
- h('div', { className: 'card', style: { marginBottom: 20 } },
3093
- h('div', { className: 'card-header' }, h('span', null, 'Pending Approvals')),
3094
- pendingApprovals.length > 0
3095
- ? h('div', { className: 'card-body' },
3096
- h('div', { style: { display: 'grid', gap: 12 } },
3097
- pendingApprovals.map(function(a, i) {
3098
- var riskLevel = a.riskLevel || a.risk || 'medium';
3099
- return h('div', { key: a.id || i, style: { padding: '14px 18px', background: 'var(--bg-tertiary)', borderRadius: 8, border: '1px solid var(--border)' } },
3100
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 } },
3101
- h('span', { className: 'badge badge-info' }, a.type || a.actionType || 'action'),
3102
- h('span', { className: riskBadgeClass(riskLevel) }, riskLevel),
3103
- a.createdAt && h('span', { style: { fontSize: 12, color: 'var(--text-muted)', marginLeft: 'auto' } }, new Date(a.createdAt).toLocaleString())
3104
- ),
3105
- h('div', { style: { fontSize: 13, color: 'var(--text-secondary)', marginBottom: 12 } }, a.description || a.reason || 'No description'),
3106
- h('div', { style: { display: 'flex', gap: 8 } },
3107
- h('button', { className: 'btn btn-primary btn-sm', onClick: function() { approveRequest(a.id); } }, I.check(), ' Approve'),
3108
- h('button', { className: 'btn btn-danger btn-sm', onClick: function() { rejectRequest(a.id); } }, I.x(), ' Reject')
3109
- )
3110
- );
3111
- })
3112
- )
3113
- )
3114
- : h('div', { className: 'card-body' },
3115
- h('div', { style: { textAlign: 'center', padding: 20, color: 'var(--text-muted)', fontSize: 13 } }, 'No pending approvals.')
3116
- )
3147
+ pendingApprovals.length > 0 && h('div', { className: 'card', style: { marginBottom: 12 } },
3148
+ h('div', { className: 'card-header' }, h('span', null, 'Pending Approvals (' + pendingApprovals.length + ')')),
3149
+ h('div', { style: { padding: 0 } },
3150
+ pendingApprovals.map(function(a) {
3151
+ return h('div', { key: a.id, style: { display: 'flex', gap: 10, padding: '10px 16px', borderBottom: '1px solid var(--border)', fontSize: 12, alignItems: 'center' } },
3152
+ h('span', { style: { flex: 1 } },
3153
+ h('div', { style: { fontWeight: 500, marginBottom: 2 } }, a.action || a.description || 'Pending approval'),
3154
+ h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, a.requestedAt ? new Date(a.requestedAt).toLocaleString() : '')
3155
+ ),
3156
+ h('button', { className: 'btn btn-primary btn-sm', style: { height: 26 }, onClick: function() { approveRequest(a.id); } }, 'Approve'),
3157
+ h('button', { className: 'btn btn-secondary btn-sm', style: { height: 26 }, onClick: function() { rejectRequest(a.id); } }, 'Reject')
3158
+ );
3159
+ })
3160
+ )
3117
3161
  ),
3118
-
3119
- // Approval History
3120
- h('div', { className: 'card', style: { marginBottom: 20 } },
3162
+ h('div', { className: 'card' },
3121
3163
  h('div', { className: 'card-header' }, h('span', null, 'Approval History')),
3122
3164
  approvalHistory.length > 0
3123
- ? h('div', { className: 'card-body-flush' },
3124
- h('table', { className: 'data-table' },
3125
- h('thead', null,
3126
- h('tr', null,
3127
- h('th', null, 'Type'),
3128
- h('th', null, 'Decision'),
3129
- h('th', null, 'Decided By'),
3130
- h('th', null, 'Date')
3131
- )
3132
- ),
3133
- h('tbody', null,
3134
- approvalHistory.map(function(a, i) {
3135
- var decision = a.decision || a.status || 'unknown';
3136
- var decisionColor = decision === 'approved' ? 'badge-success' : decision === 'rejected' ? 'badge-danger' : 'badge-neutral';
3137
- var decidedAt = a.decidedAt || a.updatedAt || a.createdAt;
3138
-
3139
- return h('tr', { key: a.id || i },
3140
- h('td', null, h('span', { className: 'badge badge-info' }, a.type || a.actionType || 'action')),
3141
- h('td', null, h('span', { className: 'badge ' + decisionColor }, decision)),
3142
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, a.decidedBy || a.reviewer || '-'),
3143
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, decidedAt ? new Date(decidedAt).toLocaleString() : '-')
3144
- );
3145
- })
3146
- )
3165
+ ? h('div', { style: { padding: 0 } },
3166
+ approvalHistory.map(function(a, i) {
3167
+ var status = a.status || a.decision || 'unknown';
3168
+ return h('div', { key: a.id || i, style: { display: 'flex', gap: 10, padding: '8px 16px', borderBottom: '1px solid var(--border)', fontSize: 12, alignItems: 'center' } },
3169
+ h('span', { style: { color: 'var(--text-muted)', minWidth: 130 } }, a.decidedAt ? new Date(a.decidedAt).toLocaleString() : '-'),
3170
+ h('span', { className: 'badge ' + (status === 'approved' ? 'badge-success' : status === 'rejected' ? 'badge-danger' : 'badge-neutral') }, status),
3171
+ h('span', { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, a.action || a.description || '-'),
3172
+ h('span', { style: { color: 'var(--text-muted)' } }, a.decidedBy || '')
3173
+ );
3174
+ })
3175
+ )
3176
+ : h('div', { style: { padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 } }, 'No approval history')
3177
+ )
3178
+ ),
3179
+
3180
+ // ─── Create/Edit Rule Modal ─────────────────────────
3181
+ showCreate && h('div', { className: 'modal-overlay', onClick: function() { setShowCreate(false); } },
3182
+ h('div', { className: 'modal', style: { maxWidth: 520 }, onClick: function(e) { e.stopPropagation(); } },
3183
+ h('div', { className: 'modal-header' },
3184
+ h('h2', null, editRule ? 'Edit Rule' : 'Create Guardrail Rule'),
3185
+ h('button', { className: 'btn btn-ghost btn-icon', onClick: function() { setShowCreate(false); } }, I.x())
3186
+ ),
3187
+ h('div', { className: 'modal-body' },
3188
+ h('div', { className: 'form-group' },
3189
+ h('label', { className: 'form-label' }, 'Name *'),
3190
+ h('input', { className: 'input', placeholder: 'e.g. High Error Rate Alert', value: ruleForm.name, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { name: e.target.value })); } })
3191
+ ),
3192
+ h('div', { className: 'form-group' },
3193
+ h('label', { className: 'form-label' }, 'Description'),
3194
+ h('input', { className: 'input', placeholder: 'What this rule monitors...', value: ruleForm.description, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { description: e.target.value })); } })
3195
+ ),
3196
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
3197
+ h('div', { className: 'form-group' },
3198
+ h('label', { className: 'form-label' }, 'Category'),
3199
+ h('select', { className: 'input', value: ruleForm.category, onChange: function(e) {
3200
+ var cat = CATEGORIES.find(function(c) { return c.value === e.target.value; });
3201
+ setRuleForm(Object.assign({}, ruleForm, { category: e.target.value, ruleType: cat ? cat.types[0] : ruleForm.ruleType }));
3202
+ } },
3203
+ CATEGORIES.map(function(c) { return h('option', { key: c.value, value: c.value }, c.label); })
3204
+ )
3205
+ ),
3206
+ h('div', { className: 'form-group' },
3207
+ h('label', { className: 'form-label' }, 'Rule Type'),
3208
+ h('select', { className: 'input', value: ruleForm.ruleType, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { ruleType: e.target.value })); } },
3209
+ (CATEGORIES.find(function(c) { return c.value === ruleForm.category; })?.types || []).map(function(t) { return h('option', { key: t, value: t }, TYPE_LABELS[t] || t); })
3210
+ )
3211
+ )
3212
+ ),
3213
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 } },
3214
+ h('div', { className: 'form-group' },
3215
+ h('label', { className: 'form-label' }, 'Action'),
3216
+ h('select', { className: 'input', value: ruleForm.action, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { action: e.target.value })); } },
3217
+ h('option', { value: 'alert' }, 'Alert'),
3218
+ h('option', { value: 'notify' }, 'Notify'),
3219
+ h('option', { value: 'log' }, 'Log'),
3220
+ h('option', { value: 'pause' }, 'Pause Agent'),
3221
+ h('option', { value: 'kill' }, 'Kill Agent')
3222
+ )
3223
+ ),
3224
+ h('div', { className: 'form-group' },
3225
+ h('label', { className: 'form-label' }, 'Severity'),
3226
+ h('select', { className: 'input', value: ruleForm.severity, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { severity: e.target.value })); } },
3227
+ h('option', { value: 'low' }, 'Low'),
3228
+ h('option', { value: 'medium' }, 'Medium'),
3229
+ h('option', { value: 'high' }, 'High'),
3230
+ h('option', { value: 'critical' }, 'Critical')
3147
3231
  )
3232
+ ),
3233
+ h('div', { className: 'form-group' },
3234
+ h('label', { className: 'form-label' }, 'Cooldown (min)'),
3235
+ h('input', { className: 'input', type: 'number', value: ruleForm.cooldownMinutes, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { cooldownMinutes: e.target.value })); } })
3236
+ )
3237
+ ),
3238
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
3239
+ h('div', { className: 'form-group' },
3240
+ h('label', { className: 'form-label' }, 'Threshold'),
3241
+ h('input', { className: 'input', type: 'number', placeholder: 'e.g. 10', value: ruleForm.threshold, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { threshold: e.target.value })); } })
3242
+ ),
3243
+ h('div', { className: 'form-group' },
3244
+ h('label', { className: 'form-label' }, 'Window (min)'),
3245
+ h('input', { className: 'input', type: 'number', placeholder: 'e.g. 60', value: ruleForm.windowMinutes, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { windowMinutes: e.target.value })); } })
3148
3246
  )
3149
- : h('div', { className: 'card-body' },
3150
- h('div', { style: { textAlign: 'center', padding: 20, color: 'var(--text-muted)', fontSize: 13 } }, 'No approval history.')
3247
+ ),
3248
+ (ruleForm.category === 'communication' || ruleForm.category === 'security') && h(Fragment, null,
3249
+ h('div', { className: 'form-group' },
3250
+ h('label', { className: 'form-label' }, 'Keywords (comma-separated)'),
3251
+ h('input', { className: 'input', placeholder: 'confidential, secret, password', value: ruleForm.keywords, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { keywords: e.target.value })); } })
3252
+ ),
3253
+ h('div', { className: 'form-group' },
3254
+ h('label', { className: 'form-label' }, 'Patterns (comma-separated regex)'),
3255
+ h('input', { className: 'input', placeholder: '\\d{4}-\\d{4}-\\d{4}-\\d{4}', value: ruleForm.patterns, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { patterns: e.target.value })); } })
3151
3256
  )
3257
+ ),
3258
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 } },
3259
+ h('input', { type: 'checkbox', checked: ruleForm.enabled, onChange: function(e) { setRuleForm(Object.assign({}, ruleForm, { enabled: e.target.checked })); } }),
3260
+ h('label', { style: { fontSize: 13 } }, 'Enabled')
3261
+ ),
3262
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 8 } }, 'This rule will be scoped to this agent only. To create org-wide rules, use the Guardrails page in the sidebar.')
3263
+ ),
3264
+ h('div', { className: 'modal-footer' },
3265
+ h('button', { className: 'btn btn-ghost', onClick: function() { setShowCreate(false); } }, 'Cancel'),
3266
+ h('button', { className: 'btn btn-primary', onClick: saveRule }, editRule ? 'Update Rule' : 'Create Rule')
3267
+ )
3152
3268
  )
3153
3269
  )
3154
3270
  );
3155
3271
  }
3156
-
3157
- // ════════════════════════════════════════════════════════════
3158
- // CONFIGURATION SECTION — Model, Description, Soul, General
3159
- // ════════════════════════════════════════════════════════════
3160
-
3161
3272
  function ConfigurationSection(props) {
3162
3273
  var agentId = props.agentId;
3163
3274
  var engineAgent = props.engineAgent;