@agenticmail/enterprise 0.5.164 → 0.5.166

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.
@@ -1998,13 +1998,37 @@ function MemorySection(props) {
1998
1998
  var filterCategory = _filterCat[0]; var setFilterCategory = _filterCat[1];
1999
1999
  var _filterImp = useState('');
2000
2000
  var filterImportance = _filterImp[0]; var setFilterImportance = _filterImp[1];
2001
+ var _dateFrom = useState('');
2002
+ var dateFrom = _dateFrom[0]; var setDateFrom = _dateFrom[1];
2003
+ var _dateTo = useState('');
2004
+ var dateTo = _dateTo[0]; var setDateTo = _dateTo[1];
2005
+ var _page = useState(1);
2006
+ var page = _page[0]; var setPage = _page[1];
2007
+ var _expanded = useState(null);
2008
+ var expandedId = _expanded[0]; var setExpandedId = _expanded[1];
2001
2009
  var _showCreate = useState(false);
2002
2010
  var showCreateModal = _showCreate[0]; var setShowCreateModal = _showCreate[1];
2003
2011
  var _form = useState({ title: '', content: '', category: 'org_knowledge', importance: 'normal', tags: '' });
2004
2012
  var createForm = _form[0]; var setCreateForm = _form[1];
2005
2013
 
2014
+ var PAGE_SIZE = 10;
2015
+
2016
+ var MEMORY_CATEGORIES = [
2017
+ { value: 'org_knowledge', label: 'Org Knowledge' },
2018
+ { value: 'preference', label: 'Preference' },
2019
+ { value: 'interaction_pattern', label: 'Interaction Pattern' },
2020
+ { value: 'context', label: 'Context' },
2021
+ { value: 'skill', label: 'Skill' },
2022
+ { value: 'processed_email', label: 'Processed Email' },
2023
+ { value: 'procedure', label: 'Procedure' },
2024
+ { value: 'relationship', label: 'Relationship' },
2025
+ { value: 'reflection', label: 'Reflection' },
2026
+ { value: 'domain_expertise', label: 'Domain Expertise' },
2027
+ { value: 'error_pattern', label: 'Error Pattern' }
2028
+ ];
2029
+
2006
2030
  var buildQueryParams = function() {
2007
- var params = '?limit=50';
2031
+ var params = '?limit=200';
2008
2032
  if (searchQuery) params += '&search=' + encodeURIComponent(searchQuery);
2009
2033
  if (filterCategory) params += '&category=' + filterCategory;
2010
2034
  if (filterImportance) params += '&importance=' + filterImportance;
@@ -2012,9 +2036,8 @@ function MemorySection(props) {
2012
2036
  };
2013
2037
 
2014
2038
  var loadMemories = function() {
2015
- var params = buildQueryParams();
2016
- engineCall('/memory/agent/' + agentId + params)
2017
- .then(function(d) { setMemories(d.memories || []); })
2039
+ engineCall('/memory/agent/' + agentId + buildQueryParams())
2040
+ .then(function(d) { setMemories(d.memories || []); setPage(1); })
2018
2041
  .catch(function() {});
2019
2042
  };
2020
2043
 
@@ -2024,38 +2047,24 @@ function MemorySection(props) {
2024
2047
  .catch(function() {});
2025
2048
  };
2026
2049
 
2027
- var loadAll = function() {
2028
- loadMemories();
2029
- loadStats();
2030
- };
2050
+ var loadAll = function() { loadMemories(); loadStats(); };
2031
2051
 
2032
- useEffect(loadAll, []);
2052
+ useEffect(function() { loadAll(); }, [agentId]);
2033
2053
  useEffect(function() { loadMemories(); }, [filterCategory, filterImportance]);
2034
2054
 
2035
- var handleSearch = function() {
2036
- loadMemories();
2037
- };
2055
+ var handleSearch = function() { loadMemories(); };
2038
2056
 
2039
2057
  var createMemory = function() {
2040
- if (!createForm.title || !createForm.content) { toast('Title and content are required', 'error'); return; }
2041
- var tagsArray = createForm.tags ? createForm.tags.split(',').map(function(s) { return s.trim(); }).filter(Boolean) : [];
2042
2058
  var body = {
2043
2059
  agentId: agentId,
2044
- orgId: getOrgId(),
2045
2060
  title: createForm.title,
2046
2061
  content: createForm.content,
2047
2062
  category: createForm.category,
2048
2063
  importance: createForm.importance,
2049
- source: 'admin',
2050
- tags: tagsArray
2064
+ tags: createForm.tags ? createForm.tags.split(',').map(function(t) { return t.trim(); }).filter(Boolean) : []
2051
2065
  };
2052
2066
  engineCall('/memory', { method: 'POST', body: JSON.stringify(body) })
2053
- .then(function() {
2054
- toast('Memory created', 'success');
2055
- setShowCreateModal(false);
2056
- setCreateForm({ title: '', content: '', category: 'org_knowledge', importance: 'normal', tags: '' });
2057
- loadAll();
2058
- })
2067
+ .then(function() { toast('Memory created', 'success'); setShowCreateModal(false); setCreateForm({ title: '', content: '', category: 'org_knowledge', importance: 'normal', tags: '' }); loadAll(); })
2059
2068
  .catch(function(e) { toast(e.message, 'error'); });
2060
2069
  };
2061
2070
 
@@ -2063,7 +2072,7 @@ function MemorySection(props) {
2063
2072
  showConfirm({
2064
2073
  title: 'Delete Memory',
2065
2074
  message: 'Are you sure you want to delete this memory entry? This action cannot be undone.',
2066
- danger: true,
2075
+ warning: true,
2067
2076
  confirmText: 'Delete'
2068
2077
  }).then(function(confirmed) {
2069
2078
  if (!confirmed) return;
@@ -2076,13 +2085,13 @@ function MemorySection(props) {
2076
2085
  var pruneStale = function() {
2077
2086
  showConfirm({
2078
2087
  title: 'Prune Stale Memories',
2079
- message: 'This will remove expired and stale memory entries for this agent. This action cannot be undone.',
2088
+ message: 'This will remove expired and stale memory entries for this agent.',
2080
2089
  warning: true,
2081
2090
  confirmText: 'Prune'
2082
2091
  }).then(function(confirmed) {
2083
2092
  if (!confirmed) return;
2084
2093
  engineCall('/memory/agent/' + agentId + '/prune', { method: 'POST' })
2085
- .then(function(d) { toast('Pruned ' + (d.pruned || 0) + ' entries', 'success'); loadAll(); })
2094
+ .then(function(d) { toast('Pruned ' + (d.deleted || 0) + ' entries', 'success'); loadAll(); })
2086
2095
  .catch(function(e) { toast(e.message, 'error'); });
2087
2096
  });
2088
2097
  };
@@ -2090,7 +2099,7 @@ function MemorySection(props) {
2090
2099
  var runDecay = function() {
2091
2100
  showConfirm({
2092
2101
  title: 'Run Confidence Decay',
2093
- message: 'This will reduce the confidence score of memories that have not been accessed recently. Decay rate: 10%.',
2102
+ message: 'This will reduce confidence of memories not accessed recently. Decay rate: 10%.',
2094
2103
  warning: true,
2095
2104
  confirmText: 'Run Decay'
2096
2105
  }).then(function(confirmed) {
@@ -2101,116 +2110,161 @@ function MemorySection(props) {
2101
2110
  });
2102
2111
  };
2103
2112
 
2104
- // Derive stats values
2113
+ // Date filter client-side
2114
+ var filtered = memories;
2115
+ if (dateFrom) {
2116
+ var fromTs = new Date(dateFrom).getTime();
2117
+ filtered = filtered.filter(function(m) { return m.createdAt && new Date(m.createdAt).getTime() >= fromTs; });
2118
+ }
2119
+ if (dateTo) {
2120
+ var toTs = new Date(dateTo + 'T23:59:59').getTime();
2121
+ filtered = filtered.filter(function(m) { return m.createdAt && new Date(m.createdAt).getTime() <= toTs; });
2122
+ }
2123
+
2124
+ var totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
2125
+ var paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
2126
+
2127
+ // Stats
2105
2128
  var totalMemories = memoryStats ? (memoryStats.totalEntries || memoryStats.total || 0) : 0;
2106
2129
  var categoriesUsed = memoryStats && memoryStats.byCategory ? Object.keys(memoryStats.byCategory).length : 0;
2107
2130
  var avgConfidence = memoryStats && memoryStats.avgConfidence != null ? ((memoryStats.avgConfidence * 100).toFixed(0) + '%') : '-';
2108
2131
  var sourcesCount = memoryStats && memoryStats.bySource ? Object.keys(memoryStats.bySource).length : 0;
2109
2132
 
2133
+ var catColor = function(c) {
2134
+ var m = { preference: '#8b5cf6', interaction_pattern: '#ec4899', context: '#3b82f6', skill: '#10b981', processed_email: '#6366f1', org_knowledge: '#f59e0b', procedure: '#14b8a6', relationship: '#f43f5e', reflection: '#a855f7', domain_expertise: '#0ea5e9', error_pattern: '#ef4444' };
2135
+ return m[c] || '#64748b';
2136
+ };
2137
+ var impColor = function(i) {
2138
+ var m = { critical: '#ef4444', high: '#f43f5e', normal: '#3b82f6', low: '#64748b' };
2139
+ return m[i] || '#64748b';
2140
+ };
2141
+
2142
+ var fmtDate = function(d) { if (!d) return '-'; var dt = new Date(d); return dt.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); };
2143
+ var fmtTime = function(d) { if (!d) return ''; var dt = new Date(d); return dt.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); };
2144
+
2110
2145
  return h('div', { className: 'card' },
2111
2146
  h('div', { className: 'card-header', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } },
2112
2147
  h('h3', { style: { margin: 0, fontSize: 15, fontWeight: 600 } }, 'Memory'),
2113
- h('button', { className: 'btn btn-ghost btn-sm', onClick: loadAll }, I.refresh(), ' Refresh')
2148
+ h('div', { style: { display: 'flex', gap: 6 } },
2149
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: pruneStale, title: 'Prune stale entries' }, I.trash()),
2150
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: runDecay, title: 'Run confidence decay' }, I.clock()),
2151
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: loadAll }, I.refresh()),
2152
+ h('button', { className: 'btn btn-primary btn-sm', onClick: function() { setShowCreateModal(true); } }, I.plus(), ' Add')
2153
+ )
2114
2154
  ),
2115
- h('div', { className: 'card-body' },
2116
-
2117
- // Stats Row
2118
- h('div', { className: 'stat-grid', style: { marginBottom: 16 } },
2119
- h('div', { className: 'stat-card' },
2120
- h('div', { className: 'stat-value' }, totalMemories),
2121
- h('div', { className: 'stat-label' }, 'Total Memories')
2122
- ),
2123
- h('div', { className: 'stat-card' },
2124
- h('div', { className: 'stat-value' }, categoriesUsed),
2125
- h('div', { className: 'stat-label' }, 'Categories Used')
2126
- ),
2127
- h('div', { className: 'stat-card' },
2128
- h('div', { className: 'stat-value' }, avgConfidence),
2129
- h('div', { className: 'stat-label' }, 'Avg Confidence')
2130
- ),
2131
- h('div', { className: 'stat-card' },
2132
- h('div', { className: 'stat-value' }, sourcesCount),
2133
- h('div', { className: 'stat-label' }, 'Sources')
2134
- )
2155
+ h('div', { className: 'card-body', style: { padding: 0 } },
2156
+
2157
+ // Compact stats bar
2158
+ h('div', { style: { display: 'flex', gap: 24, padding: '10px 16px', borderBottom: '1px solid var(--border)', fontSize: 13 } },
2159
+ h('span', { style: { color: 'var(--text-muted)' } }, 'Total: ', h('strong', null, totalMemories)),
2160
+ h('span', { style: { color: 'var(--text-muted)' } }, 'Categories: ', h('strong', null, categoriesUsed)),
2161
+ h('span', { style: { color: 'var(--text-muted)' } }, 'Avg Conf: ', h('strong', null, avgConfidence)),
2162
+ h('span', { style: { color: 'var(--text-muted)' } }, 'Sources: ', h('strong', null, sourcesCount)),
2163
+ h('div', { style: { flex: 1 } }),
2164
+ h('span', { style: { color: 'var(--text-muted)' } }, 'Showing ', h('strong', null, filtered.length), ' of ', totalMemories)
2135
2165
  ),
2136
2166
 
2137
- // Filter Bar
2138
- h('div', { style: { display: 'flex', gap: 8, marginBottom: 16, flexWrap: 'wrap', alignItems: 'center' } },
2139
- h('div', { style: { position: 'relative', flex: 1, minWidth: 180 } },
2140
- h('input', {
2141
- className: 'input',
2142
- placeholder: 'Search memories...',
2143
- value: searchQuery,
2144
- onChange: function(e) { setSearchQuery(e.target.value); },
2145
- onKeyDown: function(e) { if (e.key === 'Enter') handleSearch(); }
2146
- })
2147
- ),
2148
- h('button', { className: 'btn btn-ghost btn-sm', onClick: handleSearch }, I.search()),
2149
- h('select', { className: 'input', style: { maxWidth: 170 }, value: filterCategory, onChange: function(e) { setFilterCategory(e.target.value); } },
2167
+ // Filter row
2168
+ h('div', { style: { display: 'flex', gap: 6, padding: '8px 16px', borderBottom: '1px solid var(--border)', flexWrap: 'wrap', alignItems: 'center' } },
2169
+ h('input', {
2170
+ className: 'input', style: { flex: 1, minWidth: 140, height: 30, fontSize: 12 },
2171
+ placeholder: 'Search...', value: searchQuery,
2172
+ onChange: function(e) { setSearchQuery(e.target.value); },
2173
+ onKeyDown: function(e) { if (e.key === 'Enter') handleSearch(); }
2174
+ }),
2175
+ h('select', { className: 'input', style: { width: 130, height: 30, fontSize: 12 }, value: filterCategory, onChange: function(e) { setFilterCategory(e.target.value); } },
2150
2176
  h('option', { value: '' }, 'All Categories'),
2151
2177
  MEMORY_CATEGORIES.map(function(c) { return h('option', { key: c.value, value: c.value }, c.label); })
2152
2178
  ),
2153
- h('select', { className: 'input', style: { maxWidth: 150 }, value: filterImportance, onChange: function(e) { setFilterImportance(e.target.value); } },
2154
- h('option', { value: '' }, 'All Importance'),
2179
+ h('select', { className: 'input', style: { width: 110, height: 30, fontSize: 12 }, value: filterImportance, onChange: function(e) { setFilterImportance(e.target.value); } },
2180
+ h('option', { value: '' }, 'All Levels'),
2155
2181
  h('option', { value: 'critical' }, 'Critical'),
2156
2182
  h('option', { value: 'high' }, 'High'),
2157
2183
  h('option', { value: 'normal' }, 'Normal'),
2158
2184
  h('option', { value: 'low' }, 'Low')
2159
2185
  ),
2160
- h('div', { style: { flex: '0 0 auto' } }),
2161
- h('button', { className: 'btn btn-primary btn-sm', onClick: function() { setShowCreateModal(true); } }, I.plus(), ' Create Memory'),
2162
- h('button', { className: 'btn btn-ghost btn-sm', style: { color: 'var(--warning)' }, onClick: pruneStale }, I.trash(), ' Prune Stale'),
2163
- h('button', { className: 'btn btn-ghost btn-sm', style: { color: 'var(--warning)' }, onClick: runDecay }, I.clock(), ' Run Decay')
2186
+ h('input', { type: 'date', className: 'input', style: { width: 120, height: 30, fontSize: 12 }, value: dateFrom, onChange: function(e) { setDateFrom(e.target.value); setPage(1); }, title: 'From date' }),
2187
+ h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, ''),
2188
+ h('input', { type: 'date', className: 'input', style: { width: 120, height: 30, fontSize: 12 }, value: dateTo, onChange: function(e) { setDateTo(e.target.value); setPage(1); }, title: 'To date' }),
2189
+ (dateFrom || dateTo) && h('button', { className: 'btn btn-ghost btn-sm', style: { height: 30, fontSize: 11 }, onClick: function() { setDateFrom(''); setDateTo(''); } }, 'Clear')
2164
2190
  ),
2165
2191
 
2166
- // Memory Cards List
2167
- memories.length === 0
2168
- ? h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'No memories found for this agent')
2169
- : h('div', { style: { display: 'grid', gap: 10 } },
2170
- memories.map(function(m) {
2171
- var truncatedContent = m.content && m.content.length > 200 ? m.content.substring(0, 200) + '...' : (m.content || '');
2172
- var tags = m.tags || [];
2173
- var confidence = m.confidence != null ? ((m.confidence * 100).toFixed(0) + '%') : '-';
2174
- var created = m.createdAt ? new Date(m.createdAt).toLocaleDateString() : '-';
2175
- var lastAccessed = m.lastAccessedAt ? new Date(m.lastAccessedAt).toLocaleDateString() : '-';
2176
-
2177
- return h('div', { key: m.id, style: { padding: '14px 18px', background: 'var(--bg-tertiary)', borderRadius: 8, border: '1px solid var(--border)' } },
2178
- // Title
2179
- h('div', { style: { fontWeight: 600, fontSize: 14, marginBottom: 6 } }, m.title || 'Untitled'),
2180
-
2181
- // Category + Importance + Source badges row
2182
- h('div', { style: { display: 'flex', gap: 6, marginBottom: 8, flexWrap: 'wrap' } },
2183
- h('span', { style: { display: 'inline-block', padding: '2px 8px', borderRadius: 4, fontSize: 11, fontWeight: 600, color: '#fff', background: memCatColor(m.category) } }, memCatLabel(m.category)),
2184
- h('span', { style: { display: 'inline-block', padding: '2px 8px', borderRadius: 4, fontSize: 11, fontWeight: 600, color: '#fff', background: importanceBadgeColor(m.importance) } }, m.importance || 'normal'),
2185
- m.source && h('span', { className: 'badge badge-neutral' }, m.source)
2192
+ // Table-style compact list
2193
+ filtered.length === 0
2194
+ ? h('div', { style: { padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 } }, 'No memories found')
2195
+ : h(Fragment, null,
2196
+ // Header row
2197
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 100px 70px 60px 70px 36px', gap: 8, padding: '6px 16px', borderBottom: '1px solid var(--border)', fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.5px' } },
2198
+ h('span', null, 'Memory'),
2199
+ h('span', null, 'Category'),
2200
+ h('span', null, 'Level'),
2201
+ h('span', null, 'Conf'),
2202
+ h('span', null, 'Date'),
2203
+ h('span', null, '')
2204
+ ),
2205
+ // Rows
2206
+ paged.map(function(m) {
2207
+ var isExpanded = expandedId === m.id;
2208
+ var conf = m.confidence != null ? Math.round(m.confidence * 100) : 0;
2209
+ var confBar = conf >= 80 ? 'var(--success)' : conf >= 50 ? 'var(--warning)' : 'var(--danger)';
2210
+ return h('div', { key: m.id },
2211
+ // Compact row
2212
+ h('div', {
2213
+ style: { display: 'grid', gridTemplateColumns: '1fr 100px 70px 60px 70px 36px', gap: 8, padding: '8px 16px', borderBottom: '1px solid var(--border)', cursor: 'pointer', fontSize: 13, alignItems: 'center', transition: 'background 0.1s', background: isExpanded ? 'var(--bg-tertiary)' : 'transparent' },
2214
+ onClick: function() { setExpandedId(isExpanded ? null : m.id); },
2215
+ onMouseEnter: function(e) { if (!isExpanded) e.currentTarget.style.background = 'var(--bg-secondary)'; },
2216
+ onMouseLeave: function(e) { if (!isExpanded) e.currentTarget.style.background = 'transparent'; }
2217
+ },
2218
+ // Title + preview
2219
+ h('div', { style: { overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' } },
2220
+ h('span', { style: { fontWeight: 500 } }, m.title || 'Untitled'),
2221
+ m.content && h('span', { style: { color: 'var(--text-muted)', marginLeft: 8, fontSize: 12 } }, m.content.substring(0, 60) + (m.content.length > 60 ? '...' : ''))
2186
2222
  ),
2187
-
2188
- // Content (truncated)
2189
- h('div', { style: { fontSize: 13, color: 'var(--text-muted)', marginBottom: 8, lineHeight: 1.5, overflow: 'hidden', textOverflow: 'ellipsis' } }, truncatedContent),
2190
-
2191
- // Tags row
2192
- tags.length > 0 && h('div', { style: { display: 'flex', gap: 4, marginBottom: 8, flexWrap: 'wrap' } },
2193
- tags.map(function(tag, ti) {
2194
- return h('span', { key: ti, className: 'badge badge-neutral', style: { fontSize: 10 } }, tag);
2195
- })
2223
+ // Category badge
2224
+ h('span', { style: { display: 'inline-block', padding: '1px 6px', borderRadius: 3, fontSize: 10, fontWeight: 600, color: '#fff', background: catColor(m.category), whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, (m.category || '').replace(/_/g, ' ')),
2225
+ // Importance
2226
+ h('span', { style: { fontSize: 11, color: impColor(m.importance), fontWeight: 500 } }, m.importance || 'normal'),
2227
+ // Confidence bar
2228
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
2229
+ h('div', { style: { flex: 1, height: 4, borderRadius: 2, background: 'var(--border)' } },
2230
+ h('div', { style: { width: conf + '%', height: '100%', borderRadius: 2, background: confBar } })
2231
+ ),
2232
+ h('span', { style: { fontSize: 10, color: 'var(--text-muted)', minWidth: 24 } }, conf + '%')
2196
2233
  ),
2197
-
2198
- // Meta row: confidence, created, last accessed, delete button
2199
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 16, fontSize: 11, color: 'var(--text-muted)' } },
2200
- h('span', null, 'Confidence: ', h('strong', null, confidence)),
2201
- h('span', null, 'Created: ', created),
2202
- h('span', null, 'Last accessed: ', lastAccessed),
2234
+ // Date
2235
+ h('span', { style: { fontSize: 11, color: 'var(--text-muted)', whiteSpace: 'nowrap' } }, fmtDate(m.createdAt)),
2236
+ // Expand indicator
2237
+ h('span', { style: { fontSize: 10, color: 'var(--text-muted)', textAlign: 'center' } }, isExpanded ? '▲' : '▼')
2238
+ ),
2239
+ // Expanded detail
2240
+ isExpanded && h('div', { style: { padding: '10px 16px 12px', background: 'var(--bg-tertiary)', borderBottom: '1px solid var(--border)', fontSize: 12, lineHeight: 1.6 } },
2241
+ h('div', { style: { color: 'var(--text)', marginBottom: 8, whiteSpace: 'pre-wrap', maxHeight: 200, overflow: 'auto' } }, m.content || '(empty)'),
2242
+ h('div', { style: { display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center', fontSize: 11, color: 'var(--text-muted)' } },
2243
+ h('span', null, 'Source: ', h('strong', null, m.source || '-')),
2244
+ h('span', null, 'Created: ', h('strong', null, fmtDate(m.createdAt)), ' ', fmtTime(m.createdAt)),
2245
+ m.lastAccessedAt && h('span', null, 'Last accessed: ', h('strong', null, fmtDate(m.lastAccessedAt))),
2246
+ m.tags && m.tags.length > 0 && h('span', null, 'Tags: ', m.tags.join(', ')),
2203
2247
  h('div', { style: { flex: 1 } }),
2204
- h('button', { className: 'btn btn-ghost btn-sm', style: { color: 'var(--danger)' }, onClick: function() { deleteMemory(m.id); } }, I.trash(), ' Delete')
2248
+ h('button', { className: 'btn btn-ghost btn-sm', style: { color: 'var(--danger)', height: 24, fontSize: 11 }, onClick: function(e) { e.stopPropagation(); deleteMemory(m.id); } }, I.trash(), ' Delete')
2205
2249
  )
2206
- );
2207
- })
2250
+ )
2251
+ );
2252
+ }),
2253
+
2254
+ // Pagination
2255
+ totalPages > 1 && h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 4, padding: '10px 16px', borderTop: '1px solid var(--border)' } },
2256
+ h('button', { className: 'btn btn-ghost btn-sm', disabled: page <= 1, onClick: function() { setPage(1); }, style: { fontSize: 11, height: 28 } }, '«'),
2257
+ h('button', { className: 'btn btn-ghost btn-sm', disabled: page <= 1, onClick: function() { setPage(page - 1); }, style: { fontSize: 11, height: 28 } }, '‹'),
2258
+ h('span', { style: { fontSize: 12, color: 'var(--text-muted)', padding: '0 8px' } }, 'Page ', h('strong', null, page), ' of ', h('strong', null, totalPages)),
2259
+ h('button', { className: 'btn btn-ghost btn-sm', disabled: page >= totalPages, onClick: function() { setPage(page + 1); }, style: { fontSize: 11, height: 28 } }, '›'),
2260
+ h('button', { className: 'btn btn-ghost btn-sm', disabled: page >= totalPages, onClick: function() { setPage(totalPages); }, style: { fontSize: 11, height: 28 } }, '»')
2208
2261
  )
2262
+ )
2209
2263
  ),
2210
2264
 
2211
2265
  // Create Memory Modal
2212
2266
  showCreateModal && h('div', { className: 'modal-overlay', onClick: function() { setShowCreateModal(false); } },
2213
- h('div', { className: 'modal', style: { maxWidth: 540 }, onClick: function(e) { e.stopPropagation(); } },
2267
+ h('div', { className: 'modal', style: { maxWidth: 500 }, onClick: function(e) { e.stopPropagation(); } },
2214
2268
  h('div', { className: 'modal-header' },
2215
2269
  h('h2', null, 'Create Memory'),
2216
2270
  h('button', { className: 'btn btn-ghost btn-icon', onClick: function() { setShowCreateModal(false); } }, I.x())
@@ -2222,7 +2276,7 @@ function MemorySection(props) {
2222
2276
  ),
2223
2277
  h('div', { className: 'form-group' },
2224
2278
  h('label', { className: 'form-label' }, 'Content *'),
2225
- h('textarea', { className: 'input', style: { minHeight: 120 }, placeholder: 'Memory content...', value: createForm.content, onChange: function(e) { setCreateForm(Object.assign({}, createForm, { content: e.target.value })); } })
2279
+ h('textarea', { className: 'input', style: { minHeight: 100 }, placeholder: 'Memory content...', value: createForm.content, onChange: function(e) { setCreateForm(Object.assign({}, createForm, { content: e.target.value })); } })
2226
2280
  ),
2227
2281
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
2228
2282
  h('div', { className: 'form-group' },
@@ -2243,12 +2297,12 @@ function MemorySection(props) {
2243
2297
  ),
2244
2298
  h('div', { className: 'form-group' },
2245
2299
  h('label', { className: 'form-label' }, 'Tags (comma-separated)'),
2246
- h('input', { className: 'input', placeholder: 'tag1, tag2, tag3', value: createForm.tags, onChange: function(e) { setCreateForm(Object.assign({}, createForm, { tags: e.target.value })); } })
2300
+ h('input', { className: 'input', placeholder: 'tag1, tag2', value: createForm.tags, onChange: function(e) { setCreateForm(Object.assign({}, createForm, { tags: e.target.value })); } })
2247
2301
  )
2248
2302
  ),
2249
2303
  h('div', { className: 'modal-footer' },
2250
2304
  h('button', { className: 'btn btn-ghost', onClick: function() { setShowCreateModal(false); } }, 'Cancel'),
2251
- h('button', { className: 'btn btn-primary', onClick: createMemory }, 'Create Memory')
2305
+ h('button', { className: 'btn btn-primary', onClick: createMemory }, 'Create')
2252
2306
  )
2253
2307
  )
2254
2308
  )
@@ -2733,14 +2787,14 @@ function GuardrailsSection(props) {
2733
2787
  var agents = props.agents || [];
2734
2788
  var app = useApp();
2735
2789
  var toast = app.toast;
2736
-
2737
2790
  var agentData = buildAgentDataMap(agents);
2738
2791
 
2739
- var _subTab = useState('status');
2792
+ var _subTab = useState('rules');
2740
2793
  var subTab = _subTab[0]; var setSubTab = _subTab[1];
2741
-
2742
2794
  var _guardrailStatus = useState(null);
2743
2795
  var guardrailStatus = _guardrailStatus[0]; var setGuardrailStatus = _guardrailStatus[1];
2796
+ var _rules = useState([]);
2797
+ var rules = _rules[0]; var setRules = _rules[1];
2744
2798
  var _interventions = useState([]);
2745
2799
  var interventions = _interventions[0]; var setInterventions = _interventions[1];
2746
2800
  var _dlpViolations = useState([]);
@@ -2749,33 +2803,55 @@ function GuardrailsSection(props) {
2749
2803
  var onboardingStatus = _onboardingStatus[0]; var setOnboardingStatus = _onboardingStatus[1];
2750
2804
  var _onboardingProgress = useState([]);
2751
2805
  var onboardingProgress = _onboardingProgress[0]; var setOnboardingProgress = _onboardingProgress[1];
2752
- var _pendingPolicies = useState([]);
2753
- var pendingPolicies = _pendingPolicies[0]; var setPendingPolicies = _pendingPolicies[1];
2754
2806
  var _pendingApprovals = useState([]);
2755
2807
  var pendingApprovals = _pendingApprovals[0]; var setPendingApprovals = _pendingApprovals[1];
2756
2808
  var _approvalHistory = useState([]);
2757
2809
  var approvalHistory = _approvalHistory[0]; var setApprovalHistory = _approvalHistory[1];
2758
2810
  var _loading = useState(true);
2759
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
+ };
2760
2836
 
2761
2837
  var loadAll = function() {
2762
2838
  setLoading(true);
2763
2839
  Promise.all([
2764
2840
  engineCall('/guardrails/status/' + agentId).catch(function() { return null; }),
2841
+ engineCall('/guardrails/rules?orgId=' + getOrgId()).catch(function() { return { rules: [] }; }),
2765
2842
  engineCall('/guardrails/interventions?agentId=' + agentId).catch(function() { return { interventions: [] }; }),
2766
2843
  engineCall('/dlp/violations?agentId=' + agentId).catch(function() { return { violations: [] }; }),
2767
2844
  engineCall('/onboarding/status/' + agentId).catch(function() { return null; }),
2768
2845
  engineCall('/onboarding/progress/' + agentId).catch(function() { return { progress: [] }; }),
2769
- engineCall('/onboarding/pending/' + agentId).catch(function() { return { policies: [] }; }),
2770
2846
  engineCall('/approvals/pending?agentId=' + agentId).catch(function() { return { approvals: [] }; }),
2771
2847
  engineCall('/approvals/history?agentId=' + agentId).catch(function() { return { approvals: [] }; })
2772
2848
  ]).then(function(results) {
2773
2849
  setGuardrailStatus(results[0]);
2774
- setInterventions(results[1]?.interventions || results[1] || []);
2775
- setDlpViolations(results[2]?.violations || results[2] || []);
2776
- setOnboardingStatus(results[3]);
2777
- setOnboardingProgress(results[4]?.progress || results[4] || []);
2778
- 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] || []);
2779
2855
  setPendingApprovals(results[6]?.approvals || results[6] || []);
2780
2856
  setApprovalHistory(results[7]?.approvals || results[7] || []);
2781
2857
  setLoading(false);
@@ -2784,67 +2860,146 @@ function GuardrailsSection(props) {
2784
2860
 
2785
2861
  useEffect(function() { loadAll(); }, [agentId]);
2786
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
+
2787
2869
  var pauseAgent = function() {
2788
2870
  engineCall('/guardrails/pause/' + agentId, { method: 'POST', body: JSON.stringify({ reason: 'Manual pause from dashboard' }) })
2789
2871
  .then(function() { toast('Agent paused', 'success'); loadAll(); })
2790
2872
  .catch(function(err) { toast(err.message, 'error'); });
2791
2873
  };
2792
-
2793
2874
  var resumeAgent = function() {
2794
2875
  engineCall('/guardrails/resume/' + agentId, { method: 'POST', body: JSON.stringify({ reason: 'Manual resume from dashboard' }) })
2795
2876
  .then(function() { toast('Agent resumed', 'success'); loadAll(); })
2796
2877
  .catch(function(err) { toast(err.message, 'error'); });
2797
2878
  };
2798
-
2799
2879
  var killAgent = function() {
2800
- showConfirm({
2801
- title: 'Kill Agent',
2802
- message: 'Are you sure you want to kill this agent? This will immediately terminate all running processes.',
2803
- warning: 'This action cannot be undone.',
2804
- danger: true,
2805
- confirmText: 'Kill Agent'
2806
- }).then(function(confirmed) {
2807
- 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;
2808
2882
  engineCall('/guardrails/kill/' + agentId, { method: 'POST', body: JSON.stringify({ reason: 'Manual kill from dashboard' }) })
2809
2883
  .then(function() { toast('Agent killed', 'success'); loadAll(); })
2810
2884
  .catch(function(err) { toast(err.message, 'error'); });
2811
2885
  });
2812
2886
  };
2813
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
+
2814
2954
  var initiateOnboarding = function() {
2815
2955
  engineCall('/onboarding/initiate/' + agentId, { method: 'POST', body: JSON.stringify({ orgId: getOrgId() }) })
2816
2956
  .then(function() { toast('Onboarding initiated', 'success'); loadAll(); })
2817
2957
  .catch(function(err) { toast(err.message, 'error'); });
2818
2958
  };
2819
-
2820
2959
  var forceComplete = function() {
2821
2960
  engineCall('/onboarding/force-complete/' + agentId, { method: 'POST' })
2822
2961
  .then(function() { toast('Onboarding force completed', 'success'); loadAll(); })
2823
2962
  .catch(function(err) { toast(err.message, 'error'); });
2824
2963
  };
2825
-
2826
2964
  var approveRequest = function(id) {
2827
2965
  engineCall('/approvals/' + id + '/approve', { method: 'POST', body: JSON.stringify({ decidedBy: 'dashboard-admin' }) })
2828
- .then(function() { toast('Request approved', 'success'); loadAll(); })
2966
+ .then(function() { toast('Approved', 'success'); loadAll(); })
2829
2967
  .catch(function(err) { toast(err.message, 'error'); });
2830
2968
  };
2831
-
2832
2969
  var rejectRequest = function(id) {
2833
2970
  engineCall('/approvals/' + id + '/reject', { method: 'POST', body: JSON.stringify({ decidedBy: 'dashboard-admin' }) })
2834
- .then(function() { toast('Request rejected', 'success'); loadAll(); })
2971
+ .then(function() { toast('Rejected', 'success'); loadAll(); })
2835
2972
  .catch(function(err) { toast(err.message, 'error'); });
2836
2973
  };
2837
2974
 
2838
- if (loading) {
2839
- return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading guardrails data...');
2840
- }
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...');
2841
2980
 
2842
2981
  return h(Fragment, null,
2843
2982
 
2844
- // ─── Sub-Tab Bar ────────────────────────────────────
2845
- 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 } },
2846
3001
  h('div', { className: 'tabs' },
2847
- 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 + ')'),
2848
3003
  h('div', { className: 'tab' + (subTab === 'interventions' ? ' active' : ''), onClick: function() { setSubTab('interventions'); } }, 'Interventions'),
2849
3004
  h('div', { className: 'tab' + (subTab === 'dlp' ? ' active' : ''), onClick: function() { setSubTab('dlp'); } }, 'DLP'),
2850
3005
  h('div', { className: 'tab' + (subTab === 'onboarding' ? ' active' : ''), onClick: function() { setSubTab('onboarding'); } }, 'Onboarding'),
@@ -2852,258 +3007,268 @@ function GuardrailsSection(props) {
2852
3007
  )
2853
3008
  ),
2854
3009
 
2855
- // ─── Status Tab ─────────────────────────────────────
2856
- subTab === 'status' && h('div', null,
2857
- h('div', { className: 'card', style: { marginBottom: 20 } },
2858
- h('div', { className: 'card-header' }, h('span', null, 'Guardrail Status')),
2859
- h('div', { className: 'card-body' },
2860
- guardrailStatus && guardrailStatus.paused
2861
- ? h('div', { style: { padding: '12px 16px', background: 'rgba(234, 179, 8, 0.1)', border: '1px solid var(--warning)', borderRadius: 8, marginBottom: 16 } },
2862
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 } },
2863
- h('span', { className: 'badge badge-warning' }, I.pause(), ' Paused'),
2864
- 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')
2865
3056
  ),
2866
- guardrailStatus.pauseReason && h('div', { style: { fontSize: 13, color: 'var(--text-secondary)', marginTop: 4 } }, 'Reason: ' + guardrailStatus.pauseReason)
2867
- )
2868
- : h('div', { style: { padding: '12px 16px', background: 'rgba(34, 197, 94, 0.1)', border: '1px solid var(--success)', borderRadius: 8, marginBottom: 16 } },
2869
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
2870
- h('span', { className: 'badge badge-success' }, I.shield(), ' Active')
2871
- )
3057
+ rule.description && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, rule.description)
2872
3058
  ),
2873
- h('div', { style: { display: 'flex', gap: 16, marginBottom: 16, fontSize: 13 } },
2874
- h('span', { style: { color: 'var(--text-muted)' } }, 'Interventions: ', h('strong', null, String(guardrailStatus?.interventionCount || guardrailStatus?.interventions || 0))),
2875
- guardrailStatus?.lastIntervention && h('span', { style: { color: 'var(--text-muted)' } }, 'Last: ' + new Date(guardrailStatus.lastIntervention).toLocaleString())
2876
- ),
2877
- h('div', { style: { display: 'flex', gap: 8 } },
2878
- guardrailStatus && !guardrailStatus.paused && h('button', { className: 'btn btn-secondary btn-sm', onClick: pauseAgent }, I.pause(), ' Pause'),
2879
- guardrailStatus && guardrailStatus.paused && h('button', { className: 'btn btn-primary btn-sm', onClick: resumeAgent }, I.play(), ' Resume'),
2880
- h('button', { className: 'btn btn-danger btn-sm', onClick: killAgent }, I.stop(), ' Kill')
2881
- )
2882
- )
2883
- )
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
+ })
2884
3074
  ),
2885
3075
 
2886
3076
  // ─── Interventions Tab ──────────────────────────────
2887
- subTab === 'interventions' && h('div', { className: 'card', style: { marginBottom: 20 } },
3077
+ subTab === 'interventions' && h('div', { className: 'card' },
2888
3078
  h('div', { className: 'card-header' }, h('span', null, 'Interventions')),
2889
3079
  interventions.length > 0
2890
- ? h('div', { className: 'card-body-flush' },
2891
- h('table', { className: 'data-table' },
2892
- h('thead', null,
2893
- h('tr', null,
2894
- h('th', null, 'Time'),
2895
- h('th', null, 'Type'),
2896
- h('th', null, 'Severity'),
2897
- h('th', null, 'Description'),
2898
- h('th', null, 'Resolution')
2899
- )
2900
- ),
2901
- h('tbody', null,
2902
- interventions.map(function(inv, i) {
2903
- var time = inv.timestamp || inv.createdAt;
2904
- var invType = inv.type || inv.interventionType || 'unknown';
2905
- var severity = inv.severity || 'medium';
2906
- var severityColor = severity === 'critical' ? 'badge-danger' : severity === 'high' ? 'badge-warning' : severity === 'medium' ? 'badge-info' : 'badge-neutral';
2907
- var typeColor = invType === 'block' ? 'badge-danger' : invType === 'warn' ? 'badge-warning' : invType === 'audit' ? 'badge-info' : 'badge-neutral';
2908
-
2909
- return h('tr', { key: inv.id || i },
2910
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)', whiteSpace: 'nowrap' } }, time ? new Date(time).toLocaleString() : '-'),
2911
- h('td', null, h('span', { className: 'badge ' + typeColor }, invType)),
2912
- h('td', null, h('span', { className: 'badge ' + severityColor }, severity)),
2913
- h('td', { style: { fontSize: 13, maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, inv.description || inv.message || '-'),
2914
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, inv.resolution || inv.action || '-')
2915
- );
2916
- })
2917
- )
2918
- )
2919
- )
2920
- : h('div', { className: 'card-body' },
2921
- 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
+ })
2922
3093
  )
3094
+ : h('div', { style: { padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 } }, 'No interventions recorded')
2923
3095
  ),
2924
3096
 
2925
3097
  // ─── DLP Tab ────────────────────────────────────────
2926
- subTab === 'dlp' && h('div', { className: 'card', style: { marginBottom: 20 } },
3098
+ subTab === 'dlp' && h('div', { className: 'card' },
2927
3099
  h('div', { className: 'card-header' }, h('span', null, 'DLP Violations')),
2928
3100
  dlpViolations.length > 0
2929
- ? h('div', { className: 'card-body-flush' },
2930
- h('table', { className: 'data-table' },
2931
- h('thead', null,
2932
- h('tr', null,
2933
- h('th', null, 'Time'),
2934
- h('th', null, 'Rule'),
2935
- h('th', null, 'Severity'),
2936
- h('th', null, 'Content'),
2937
- h('th', null, 'Status')
2938
- )
2939
- ),
2940
- h('tbody', null,
2941
- dlpViolations.map(function(v, i) {
2942
- var time = v.timestamp || v.createdAt || v.detectedAt;
2943
- var rule = v.rule || v.ruleName || v.ruleId || 'Unknown';
2944
- var severity = v.severity || 'medium';
2945
- var severityColor = severity === 'critical' ? 'badge-danger' : severity === 'high' ? 'badge-warning' : severity === 'medium' ? 'badge-info' : 'badge-neutral';
2946
- var content = v.content || v.matchedContent || v.snippet || '';
2947
- var truncatedContent = content.length > 80 ? content.substring(0, 80) + '...' : content;
2948
- var violationStatus = v.status || v.action || 'detected';
2949
-
2950
- return h('tr', { key: v.id || i },
2951
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)', whiteSpace: 'nowrap' } }, time ? new Date(time).toLocaleString() : '-'),
2952
- h('td', null, h('span', { className: 'badge badge-info' }, rule)),
2953
- h('td', null, h('span', { className: 'badge ' + severityColor }, severity)),
2954
- h('td', { style: { fontSize: 12, fontFamily: 'var(--font-mono, monospace)', maxWidth: 250, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'var(--text-secondary)' } }, truncatedContent || '-'),
2955
- h('td', null, h('span', { className: 'badge badge-neutral' }, violationStatus))
2956
- );
2957
- })
2958
- )
2959
- )
2960
- )
2961
- : h('div', { className: 'card-body' },
2962
- 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
+ })
2963
3113
  )
3114
+ : h('div', { style: { padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 } }, 'No DLP violations detected')
2964
3115
  ),
2965
3116
 
2966
3117
  // ─── Onboarding Tab ─────────────────────────────────
2967
3118
  subTab === 'onboarding' && h('div', null,
2968
-
2969
- // Onboarding Status Card
2970
- h('div', { className: 'card', style: { marginBottom: 20 } },
2971
- h('div', { className: 'card-header', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
2972
- h('span', null, 'Onboarding Status'),
2973
- h('div', { style: { display: 'flex', gap: 8 } },
2974
- !onboardingStatus?.onboarded && h('button', { className: 'btn btn-primary btn-sm', onClick: initiateOnboarding }, 'Start Onboarding'),
2975
- onboardingStatus && onboardingStatus.status === 'in_progress' && h('button', { className: 'btn btn-secondary btn-sm', onClick: forceComplete }, 'Force Complete')
2976
- )
2977
- ),
2978
- h('div', { className: 'card-body' },
2979
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 } },
2980
- onboardingStatus?.onboarded
2981
- ? h('span', { className: 'badge badge-success' }, I.check(), ' Onboarded')
2982
- : h('span', { className: 'badge badge-warning' }, 'Not Onboarded'),
2983
- onboardingStatus?.status && h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Status: ' + onboardingStatus.status),
2984
- onboardingStatus?.completedAt && h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Completed: ' + new Date(onboardingStatus.completedAt).toLocaleString())
2985
- )
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')
2986
3128
  )
2987
3129
  ),
2988
-
2989
- // Progress Table
2990
- onboardingProgress.length > 0 && h('div', { className: 'card', style: { marginBottom: 20 } },
2991
- h('div', { className: 'card-header' }, h('span', null, 'Onboarding Progress')),
2992
- h('div', { className: 'card-body-flush' },
2993
- h('table', { className: 'data-table' },
2994
- h('thead', null,
2995
- h('tr', null,
2996
- h('th', null, 'Policy'),
2997
- h('th', null, 'Status'),
2998
- h('th', null, 'Acknowledged')
2999
- )
3000
- ),
3001
- h('tbody', null,
3002
- onboardingProgress.map(function(p, i) {
3003
- var pStatus = p.status || 'pending';
3004
- var statusColor = pStatus === 'acknowledged' || pStatus === 'completed' ? 'badge-success' : pStatus === 'in_progress' ? 'badge-info' : 'badge-warning';
3005
-
3006
- return h('tr', { key: p.id || i },
3007
- h('td', { style: { fontWeight: 500, fontSize: 13 } }, p.policyName || p.name || p.policyId || '-'),
3008
- h('td', null, h('span', { className: 'badge ' + statusColor }, pStatus)),
3009
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, p.acknowledgedAt ? new Date(p.acknowledgedAt).toLocaleString() : '-')
3010
- );
3011
- })
3012
- )
3013
- )
3014
- )
3015
- ),
3016
-
3017
- // Pending Policies
3018
- pendingPolicies.length > 0 && h('div', { className: 'card', style: { marginBottom: 20 } },
3019
- h('div', { className: 'card-header' }, h('span', null, 'Pending Policies')),
3020
- h('div', { className: 'card-body' },
3021
- h('div', { style: { display: 'grid', gap: 8 } },
3022
- pendingPolicies.map(function(p, i) {
3023
- 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)' } },
3024
- h('span', { className: 'badge badge-warning' }, 'Pending'),
3025
- h('span', { style: { fontSize: 13, fontWeight: 500 } }, p.name || p.policyName || p.policyId || 'Unnamed Policy'),
3026
- p.category && h('span', { className: 'badge badge-neutral', style: { fontSize: 11 } }, p.category)
3027
- );
3028
- })
3029
- )
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
+ })
3030
3141
  )
3031
3142
  )
3032
3143
  ),
3033
3144
 
3034
3145
  // ─── Approvals Tab ──────────────────────────────────
3035
3146
  subTab === 'approvals' && h('div', null,
3036
-
3037
- // Pending Approvals
3038
- h('div', { className: 'card', style: { marginBottom: 20 } },
3039
- h('div', { className: 'card-header' }, h('span', null, 'Pending Approvals')),
3040
- pendingApprovals.length > 0
3041
- ? h('div', { className: 'card-body' },
3042
- h('div', { style: { display: 'grid', gap: 12 } },
3043
- pendingApprovals.map(function(a, i) {
3044
- var riskLevel = a.riskLevel || a.risk || 'medium';
3045
- return h('div', { key: a.id || i, style: { padding: '14px 18px', background: 'var(--bg-tertiary)', borderRadius: 8, border: '1px solid var(--border)' } },
3046
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 } },
3047
- h('span', { className: 'badge badge-info' }, a.type || a.actionType || 'action'),
3048
- h('span', { className: riskBadgeClass(riskLevel) }, riskLevel),
3049
- a.createdAt && h('span', { style: { fontSize: 12, color: 'var(--text-muted)', marginLeft: 'auto' } }, new Date(a.createdAt).toLocaleString())
3050
- ),
3051
- h('div', { style: { fontSize: 13, color: 'var(--text-secondary)', marginBottom: 12 } }, a.description || a.reason || 'No description'),
3052
- h('div', { style: { display: 'flex', gap: 8 } },
3053
- h('button', { className: 'btn btn-primary btn-sm', onClick: function() { approveRequest(a.id); } }, I.check(), ' Approve'),
3054
- h('button', { className: 'btn btn-danger btn-sm', onClick: function() { rejectRequest(a.id); } }, I.x(), ' Reject')
3055
- )
3056
- );
3057
- })
3058
- )
3059
- )
3060
- : h('div', { className: 'card-body' },
3061
- h('div', { style: { textAlign: 'center', padding: 20, color: 'var(--text-muted)', fontSize: 13 } }, 'No pending approvals.')
3062
- )
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
+ )
3063
3161
  ),
3064
-
3065
- // Approval History
3066
- h('div', { className: 'card', style: { marginBottom: 20 } },
3162
+ h('div', { className: 'card' },
3067
3163
  h('div', { className: 'card-header' }, h('span', null, 'Approval History')),
3068
3164
  approvalHistory.length > 0
3069
- ? h('div', { className: 'card-body-flush' },
3070
- h('table', { className: 'data-table' },
3071
- h('thead', null,
3072
- h('tr', null,
3073
- h('th', null, 'Type'),
3074
- h('th', null, 'Decision'),
3075
- h('th', null, 'Decided By'),
3076
- h('th', null, 'Date')
3077
- )
3078
- ),
3079
- h('tbody', null,
3080
- approvalHistory.map(function(a, i) {
3081
- var decision = a.decision || a.status || 'unknown';
3082
- var decisionColor = decision === 'approved' ? 'badge-success' : decision === 'rejected' ? 'badge-danger' : 'badge-neutral';
3083
- var decidedAt = a.decidedAt || a.updatedAt || a.createdAt;
3084
-
3085
- return h('tr', { key: a.id || i },
3086
- h('td', null, h('span', { className: 'badge badge-info' }, a.type || a.actionType || 'action')),
3087
- h('td', null, h('span', { className: 'badge ' + decisionColor }, decision)),
3088
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, a.decidedBy || a.reviewer || '-'),
3089
- h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, decidedAt ? new Date(decidedAt).toLocaleString() : '-')
3090
- );
3091
- })
3092
- )
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); })
3093
3210
  )
3094
3211
  )
3095
- : h('div', { className: 'card-body' },
3096
- h('div', { style: { textAlign: 'center', padding: 20, color: 'var(--text-muted)', fontSize: 13 } }, 'No approval history.')
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')
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 })); } })
3246
+ )
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 })); } })
3097
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
+ )
3098
3268
  )
3099
3269
  )
3100
3270
  );
3101
3271
  }
3102
-
3103
- // ════════════════════════════════════════════════════════════
3104
- // CONFIGURATION SECTION — Model, Description, Soul, General
3105
- // ════════════════════════════════════════════════════════════
3106
-
3107
3272
  function ConfigurationSection(props) {
3108
3273
  var agentId = props.agentId;
3109
3274
  var engineAgent = props.engineAgent;